Changed Roo/bootstrap/Modal.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @builder-top
859  * @children Roo.bootstrap.Component
860  * @parent none
861  * Bootstrap Body class
862  *
863  * @constructor
864  * Create a new body
865  * @param {Object} config The config object
866  */
867
868 Roo.bootstrap.Body = function(config){
869
870     config = config || {};
871
872     Roo.bootstrap.Body.superclass.constructor.call(this, config);
873     this.el = Roo.get(config.el ? config.el : document.body );
874     if (this.cls && this.cls.length) {
875         Roo.get(document.body).addClass(this.cls);
876     }
877 };
878
879 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
880
881     is_body : true,// just to make sure it's constructed?
882
883         autoCreate : {
884         cls: 'container'
885     },
886     onRender : function(ct, position)
887     {
888        /* Roo.log("Roo.bootstrap.Body - onRender");
889         if (this.cls && this.cls.length) {
890             Roo.get(document.body).addClass(this.cls);
891         }
892         // style??? xttr???
893         */
894     }
895
896
897
898
899 });
900 /*
901  * - LGPL
902  *
903  * button group
904  * 
905  */
906
907
908 /**
909  * @class Roo.bootstrap.ButtonGroup
910  * @extends Roo.bootstrap.Component
911  * Bootstrap ButtonGroup class
912  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913  * 
914  * @cfg {String} size lg | sm | xs (default empty normal)
915  * @cfg {String} align vertical | justified  (default none)
916  * @cfg {String} direction up | down (default down)
917  * @cfg {Boolean} toolbar false | true
918  * @cfg {Boolean} btn true | false
919  * 
920  * 
921  * @constructor
922  * Create a new Input
923  * @param {Object} config The config object
924  */
925
926 Roo.bootstrap.ButtonGroup = function(config){
927     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
928 };
929
930 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
931     
932     size: '',
933     align: '',
934     direction: '',
935     toolbar: false,
936     btn: true,
937
938     getAutoCreate : function(){
939         var cfg = {
940             cls: 'btn-group',
941             html : null
942         };
943         
944         cfg.html = this.html || cfg.html;
945         
946         if (this.toolbar) {
947             cfg = {
948                 cls: 'btn-toolbar',
949                 html: null
950             };
951             
952             return cfg;
953         }
954         
955         if (['vertical','justified'].indexOf(this.align)!==-1) {
956             cfg.cls = 'btn-group-' + this.align;
957             
958             if (this.align == 'justified') {
959                 console.log(this.items);
960             }
961         }
962         
963         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
964             cfg.cls += ' btn-group-' + this.size;
965         }
966         
967         if (this.direction == 'up') {
968             cfg.cls += ' dropup' ;
969         }
970         
971         return cfg;
972     },
973     /**
974      * Add a button to the group (similar to NavItem API.)
975      */
976     addItem : function(cfg)
977     {
978         var cn = new Roo.bootstrap.Button(cfg);
979         //this.register(cn);
980         cn.parentId = this.id;
981         cn.onRender(this.el, null);
982         return cn;
983     }
984    
985 });
986
987  /*
988  * - LGPL
989  *
990  * button
991  * 
992  */
993
994 /**
995  * @class Roo.bootstrap.Button
996  * @extends Roo.bootstrap.Component
997  * Bootstrap Button class
998  * @cfg {String} html The button content
999  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1000  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1001  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1002  * @cfg {String} size (lg|sm|xs)
1003  * @cfg {String} tag (a|input|submit)
1004  * @cfg {String} href empty or href
1005  * @cfg {Boolean} disabled default false;
1006  * @cfg {Boolean} isClose default false;
1007  * @cfg {String} glyphicon depricated - use fa
1008  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1009  * @cfg {String} badge text for badge
1010  * @cfg {String} theme (default|glow)  
1011  * @cfg {Boolean} inverse dark themed version
1012  * @cfg {Boolean} toggle is it a slidy toggle button
1013  * @cfg {Boolean} pressed   default null - if the button ahs active state
1014  * @cfg {String} ontext text for on slidy toggle state
1015  * @cfg {String} offtext text for off slidy toggle state
1016  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1017  * @cfg {Boolean} removeClass remove the standard class..
1018  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1019  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1020  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1021
1022  * @constructor
1023  * Create a new button
1024  * @param {Object} config The config object
1025  */
1026
1027
1028 Roo.bootstrap.Button = function(config){
1029     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1030     
1031     this.addEvents({
1032         // raw events
1033         /**
1034          * @event click
1035          * When a button is pressed
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          */
1039         "click" : true,
1040         /**
1041          * @event dblclick
1042          * When a button is double clicked
1043          * @param {Roo.bootstrap.Button} btn
1044          * @param {Roo.EventObject} e
1045          */
1046         "dblclick" : true,
1047          /**
1048          * @event toggle
1049          * After the button has been toggles
1050          * @param {Roo.bootstrap.Button} btn
1051          * @param {Roo.EventObject} e
1052          * @param {boolean} pressed (also available as button.pressed)
1053          */
1054         "toggle" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1059     html: false,
1060     active: false,
1061     weight: '',
1062     badge_weight: '',
1063     outline : false,
1064     size: '',
1065     tag: 'button',
1066     href: '',
1067     disabled: false,
1068     isClose: false,
1069     glyphicon: '',
1070     fa: '',
1071     badge: '',
1072     theme: 'default',
1073     inverse: false,
1074     
1075     toggle: false,
1076     ontext: 'ON',
1077     offtext: 'OFF',
1078     defaulton: true,
1079     preventDefault: true,
1080     removeClass: false,
1081     name: false,
1082     target: false,
1083     group : false,
1084      
1085     pressed : null,
1086      
1087     
1088     getAutoCreate : function(){
1089         
1090         var cfg = {
1091             tag : 'button',
1092             cls : 'roo-button',
1093             html: ''
1094         };
1095         
1096         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1097             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1098             this.tag = 'button';
1099         } else {
1100             cfg.tag = this.tag;
1101         }
1102         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103         
1104         if (this.toggle == true) {
1105             cfg={
1106                 tag: 'div',
1107                 cls: 'slider-frame roo-button',
1108                 cn: [
1109                     {
1110                         tag: 'span',
1111                         'data-on-text':'ON',
1112                         'data-off-text':'OFF',
1113                         cls: 'slider-button',
1114                         html: this.offtext
1115                     }
1116                 ]
1117             };
1118             // why are we validating the weights?
1119             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1120                 cfg.cls +=  ' ' + this.weight;
1121             }
1122             
1123             return cfg;
1124         }
1125         
1126         if (this.isClose) {
1127             cfg.cls += ' close';
1128             
1129             cfg["aria-hidden"] = true;
1130             
1131             cfg.html = "&times;";
1132             
1133             return cfg;
1134         }
1135              
1136         
1137         if (this.theme==='default') {
1138             cfg.cls = 'btn roo-button';
1139             
1140             //if (this.parentType != 'Navbar') {
1141             this.weight = this.weight.length ?  this.weight : 'default';
1142             //}
1143             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144                 
1145                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1146                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1147                 cfg.cls += ' btn-' + outline + weight;
1148                 if (this.weight == 'default') {
1149                     // BC
1150                     cfg.cls += ' btn-' + this.weight;
1151                 }
1152             }
1153         } else if (this.theme==='glow') {
1154             
1155             cfg.tag = 'a';
1156             cfg.cls = 'btn-glow roo-button';
1157             
1158             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159                 
1160                 cfg.cls += ' ' + this.weight;
1161             }
1162         }
1163    
1164         
1165         if (this.inverse) {
1166             this.cls += ' inverse';
1167         }
1168         
1169         
1170         if (this.active || this.pressed === true) {
1171             cfg.cls += ' active';
1172         }
1173         
1174         if (this.disabled) {
1175             cfg.disabled = 'disabled';
1176         }
1177         
1178         if (this.items) {
1179             Roo.log('changing to ul' );
1180             cfg.tag = 'ul';
1181             this.glyphicon = 'caret';
1182             if (Roo.bootstrap.version == 4) {
1183                 this.fa = 'caret-down';
1184             }
1185             
1186         }
1187         
1188         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189          
1190         //gsRoo.log(this.parentType);
1191         if (this.parentType === 'Navbar' && !this.parent().bar) {
1192             Roo.log('changing to li?');
1193             
1194             cfg.tag = 'li';
1195             
1196             cfg.cls = '';
1197             cfg.cn =  [{
1198                 tag : 'a',
1199                 cls : 'roo-button',
1200                 html : this.html,
1201                 href : this.href || '#'
1202             }];
1203             if (this.menu) {
1204                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1205                 cfg.cls += ' dropdown';
1206             }   
1207             
1208             delete cfg.html;
1209             
1210         }
1211         
1212        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1213         
1214         if (this.glyphicon) {
1215             cfg.html = ' ' + cfg.html;
1216             
1217             cfg.cn = [
1218                 {
1219                     tag: 'span',
1220                     cls: 'glyphicon glyphicon-' + this.glyphicon
1221                 }
1222             ];
1223         }
1224         if (this.fa) {
1225             cfg.html = ' ' + cfg.html;
1226             
1227             cfg.cn = [
1228                 {
1229                     tag: 'i',
1230                     cls: 'fa fas fa-' + this.fa
1231                 }
1232             ];
1233         }
1234         
1235         if (this.badge) {
1236             cfg.html += ' ';
1237             
1238             cfg.tag = 'a';
1239             
1240 //            cfg.cls='btn roo-button';
1241             
1242             cfg.href=this.href;
1243             
1244             var value = cfg.html;
1245             
1246             if(this.glyphicon){
1247                 value = {
1248                     tag: 'span',
1249                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1250                     html: this.html
1251                 };
1252             }
1253             if(this.fa){
1254                 value = {
1255                     tag: 'i',
1256                     cls: 'fa fas fa-' + this.fa,
1257                     html: this.html
1258                 };
1259             }
1260             
1261             var bw = this.badge_weight.length ? this.badge_weight :
1262                 (this.weight.length ? this.weight : 'secondary');
1263             bw = bw == 'default' ? 'secondary' : bw;
1264             
1265             cfg.cn = [
1266                 value,
1267                 {
1268                     tag: 'span',
1269                     cls: 'badge badge-' + bw,
1270                     html: this.badge
1271                 }
1272             ];
1273             
1274             cfg.html='';
1275         }
1276         
1277         if (this.menu) {
1278             cfg.cls += ' dropdown';
1279             cfg.html = typeof(cfg.html) != 'undefined' ?
1280                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1281         }
1282         
1283         if (cfg.tag !== 'a' && this.href !== '') {
1284             throw "Tag must be a to set href.";
1285         } else if (this.href.length > 0) {
1286             cfg.href = this.href;
1287         }
1288         
1289         if(this.removeClass){
1290             cfg.cls = '';
1291         }
1292         
1293         if(this.target){
1294             cfg.target = this.target;
1295         }
1296         
1297         return cfg;
1298     },
1299     initEvents: function() {
1300        // Roo.log('init events?');
1301 //        Roo.log(this.el.dom);
1302         // add the menu...
1303         
1304         if (typeof (this.menu) != 'undefined') {
1305             this.menu.parentType = this.xtype;
1306             this.menu.triggerEl = this.el;
1307             this.addxtype(Roo.apply({}, this.menu));
1308         }
1309
1310
1311         if (this.el.hasClass('roo-button')) {
1312              this.el.on('click', this.onClick, this);
1313              this.el.on('dblclick', this.onDblClick, this);
1314         } else {
1315              this.el.select('.roo-button').on('click', this.onClick, this);
1316              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1317              
1318         }
1319         // why?
1320         if(this.removeClass){
1321             this.el.on('click', this.onClick, this);
1322         }
1323         
1324         if (this.group === true) {
1325              if (this.pressed === false || this.pressed === true) {
1326                 // nothing
1327             } else {
1328                 this.pressed = false;
1329                 this.setActive(this.pressed);
1330             }
1331             
1332         }
1333         
1334         this.el.enableDisplayMode();
1335         
1336     },
1337     onClick : function(e)
1338     {
1339         if (this.disabled) {
1340             return;
1341         }
1342         
1343         Roo.log('button on click ');
1344         if(this.preventDefault){
1345             e.preventDefault();
1346         }
1347         
1348         if (this.group) {
1349             if (this.pressed) {
1350                 // do nothing -
1351                 return;
1352             }
1353             this.setActive(true);
1354             var pi = this.parent().items;
1355             for (var i = 0;i < pi.length;i++) {
1356                 if (this == pi[i]) {
1357                     continue;
1358                 }
1359                 if (pi[i].el.hasClass('roo-button')) {
1360                     pi[i].setActive(false);
1361                 }
1362             }
1363             this.fireEvent('click', this, e);            
1364             return;
1365         }
1366         
1367         if (this.pressed === true || this.pressed === false) {
1368             this.toggleActive(e);
1369         }
1370         
1371         
1372         this.fireEvent('click', this, e);
1373     },
1374     onDblClick: function(e)
1375     {
1376         if (this.disabled) {
1377             return;
1378         }
1379         if(this.preventDefault){
1380             e.preventDefault();
1381         }
1382         this.fireEvent('dblclick', this, e);
1383     },
1384     /**
1385      * Enables this button
1386      */
1387     enable : function()
1388     {
1389         this.disabled = false;
1390         this.el.removeClass('disabled');
1391         this.el.dom.removeAttribute("disabled");
1392     },
1393     
1394     /**
1395      * Disable this button
1396      */
1397     disable : function()
1398     {
1399         this.disabled = true;
1400         this.el.addClass('disabled');
1401         this.el.attr("disabled", "disabled")
1402     },
1403      /**
1404      * sets the active state on/off, 
1405      * @param {Boolean} state (optional) Force a particular state
1406      */
1407     setActive : function(v) {
1408         
1409         this.el[v ? 'addClass' : 'removeClass']('active');
1410         this.pressed = v;
1411     },
1412      /**
1413      * toggles the current active state 
1414      */
1415     toggleActive : function(e)
1416     {
1417         this.setActive(!this.pressed); // this modifies pressed...
1418         this.fireEvent('toggle', this, e, this.pressed);
1419     },
1420      /**
1421      * get the current active state
1422      * @return {boolean} true if it's active
1423      */
1424     isActive : function()
1425     {
1426         return this.el.hasClass('active');
1427     },
1428     /**
1429      * set the text of the first selected button
1430      */
1431     setText : function(str)
1432     {
1433         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1434     },
1435     /**
1436      * get the text of the first selected button
1437      */
1438     getText : function()
1439     {
1440         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1441     },
1442     
1443     setWeight : function(str)
1444     {
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1446         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447         this.weight = str;
1448         var outline = this.outline ? 'outline-' : '';
1449         if (str == 'default') {
1450             this.el.addClass('btn-default btn-outline-secondary');        
1451             return;
1452         }
1453         this.el.addClass('btn-' + outline + str);        
1454     }
1455     
1456     
1457 });
1458 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459
1460 Roo.bootstrap.Button.weights = [
1461     'default',
1462     'secondary' ,
1463     'primary',
1464     'success',
1465     'info',
1466     'warning',
1467     'danger',
1468     'link',
1469     'light',
1470     'dark'              
1471    
1472 ];/*
1473  * - LGPL
1474  *
1475  * column
1476  * 
1477  */
1478
1479 /**
1480  * @class Roo.bootstrap.Column
1481  * @extends Roo.bootstrap.Component
1482  * @children Roo.bootstrap.Component
1483  * Bootstrap Column class
1484  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1485  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1486  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1487  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1488  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1489  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1490  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1491  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1492  *
1493  * 
1494  * @cfg {Boolean} hidden (true|false) hide the element
1495  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1496  * @cfg {String} fa (ban|check|...) font awesome icon
1497  * @cfg {Number} fasize (1|2|....) font awsome size
1498
1499  * @cfg {String} icon (info-sign|check|...) glyphicon name
1500
1501  * @cfg {String} html content of column.
1502  * 
1503  * @constructor
1504  * Create a new Column
1505  * @param {Object} config The config object
1506  */
1507
1508 Roo.bootstrap.Column = function(config){
1509     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1510 };
1511
1512 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1513     
1514     xs: false,
1515     sm: false,
1516     md: false,
1517     lg: false,
1518     xsoff: false,
1519     smoff: false,
1520     mdoff: false,
1521     lgoff: false,
1522     html: '',
1523     offset: 0,
1524     alert: false,
1525     fa: false,
1526     icon : false,
1527     hidden : false,
1528     fasize : 1,
1529     
1530     getAutoCreate : function(){
1531         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1532         
1533         cfg = {
1534             tag: 'div',
1535             cls: 'column'
1536         };
1537         
1538         var settings=this;
1539         var sizes =   ['xs','sm','md','lg'];
1540         sizes.map(function(size ,ix){
1541             //Roo.log( size + ':' + settings[size]);
1542             
1543             if (settings[size+'off'] !== false) {
1544                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1545             }
1546             
1547             if (settings[size] === false) {
1548                 return;
1549             }
1550             
1551             if (!settings[size]) { // 0 = hidden
1552                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553                 // bootsrap4
1554                 for (var i = ix; i > -1; i--) {
1555                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1556                 }
1557                 
1558                 
1559                 return;
1560             }
1561             cfg.cls += ' col-' + size + '-' + settings[size] + (
1562                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1563             );
1564             
1565         });
1566         
1567         if (this.hidden) {
1568             cfg.cls += ' hidden';
1569         }
1570         
1571         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1572             cfg.cls +=' alert alert-' + this.alert;
1573         }
1574         
1575         
1576         if (this.html.length) {
1577             cfg.html = this.html;
1578         }
1579         if (this.fa) {
1580             var fasize = '';
1581             if (this.fasize > 1) {
1582                 fasize = ' fa-' + this.fasize + 'x';
1583             }
1584             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1585             
1586             
1587         }
1588         if (this.icon) {
1589             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1590         }
1591         
1592         return cfg;
1593     }
1594    
1595 });
1596
1597  
1598
1599  /*
1600  * - LGPL
1601  *
1602  * page container.
1603  * 
1604  */
1605
1606
1607 /**
1608  * @class Roo.bootstrap.Container
1609  * @extends Roo.bootstrap.Component
1610  * @builder-top
1611  * @children Roo.bootstrap.Component
1612  * Bootstrap Container class
1613  * @cfg {Boolean} jumbotron is it a jumbotron element
1614  * @cfg {String} html content of element
1615  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1616  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1617  * @cfg {String} header content of header (for panel)
1618  * @cfg {String} footer content of footer (for panel)
1619  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1620  * @cfg {String} tag (header|aside|section) type of HTML tag.
1621  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1622  * @cfg {String} fa font awesome icon
1623  * @cfg {String} icon (info-sign|check|...) glyphicon name
1624  * @cfg {Boolean} hidden (true|false) hide the element
1625  * @cfg {Boolean} expandable (true|false) default false
1626  * @cfg {Boolean} expanded (true|false) default true
1627  * @cfg {String} rheader contet on the right of header
1628  * @cfg {Boolean} clickable (true|false) default false
1629
1630  *     
1631  * @constructor
1632  * Create a new Container
1633  * @param {Object} config The config object
1634  */
1635
1636 Roo.bootstrap.Container = function(config){
1637     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1638     
1639     this.addEvents({
1640         // raw events
1641          /**
1642          * @event expand
1643          * After the panel has been expand
1644          * 
1645          * @param {Roo.bootstrap.Container} this
1646          */
1647         "expand" : true,
1648         /**
1649          * @event collapse
1650          * After the panel has been collapsed
1651          * 
1652          * @param {Roo.bootstrap.Container} this
1653          */
1654         "collapse" : true,
1655         /**
1656          * @event click
1657          * When a element is chick
1658          * @param {Roo.bootstrap.Container} this
1659          * @param {Roo.EventObject} e
1660          */
1661         "click" : true
1662     });
1663 };
1664
1665 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1666     
1667     jumbotron : false,
1668     well: '',
1669     panel : '',
1670     header: '',
1671     footer : '',
1672     sticky: '',
1673     tag : false,
1674     alert : false,
1675     fa: false,
1676     icon : false,
1677     expandable : false,
1678     rheader : '',
1679     expanded : true,
1680     clickable: false,
1681   
1682      
1683     getChildContainer : function() {
1684         
1685         if(!this.el){
1686             return false;
1687         }
1688         
1689         if (this.panel.length) {
1690             return this.el.select('.panel-body',true).first();
1691         }
1692         
1693         return this.el;
1694     },
1695     
1696     
1697     getAutoCreate : function(){
1698         
1699         var cfg = {
1700             tag : this.tag || 'div',
1701             html : '',
1702             cls : ''
1703         };
1704         if (this.jumbotron) {
1705             cfg.cls = 'jumbotron';
1706         }
1707         
1708         
1709         
1710         // - this is applied by the parent..
1711         //if (this.cls) {
1712         //    cfg.cls = this.cls + '';
1713         //}
1714         
1715         if (this.sticky.length) {
1716             
1717             var bd = Roo.get(document.body);
1718             if (!bd.hasClass('bootstrap-sticky')) {
1719                 bd.addClass('bootstrap-sticky');
1720                 Roo.select('html',true).setStyle('height', '100%');
1721             }
1722              
1723             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1724         }
1725         
1726         
1727         if (this.well.length) {
1728             switch (this.well) {
1729                 case 'lg':
1730                 case 'sm':
1731                     cfg.cls +=' well well-' +this.well;
1732                     break;
1733                 default:
1734                     cfg.cls +=' well';
1735                     break;
1736             }
1737         }
1738         
1739         if (this.hidden) {
1740             cfg.cls += ' hidden';
1741         }
1742         
1743         
1744         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1745             cfg.cls +=' alert alert-' + this.alert;
1746         }
1747         
1748         var body = cfg;
1749         
1750         if (this.panel.length) {
1751             cfg.cls += ' panel panel-' + this.panel;
1752             cfg.cn = [];
1753             if (this.header.length) {
1754                 
1755                 var h = [];
1756                 
1757                 if(this.expandable){
1758                     
1759                     cfg.cls = cfg.cls + ' expandable';
1760                     
1761                     h.push({
1762                         tag: 'i',
1763                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1764                     });
1765                     
1766                 }
1767                 
1768                 h.push(
1769                     {
1770                         tag: 'span',
1771                         cls : 'panel-title',
1772                         html : (this.expandable ? '&nbsp;' : '') + this.header
1773                     },
1774                     {
1775                         tag: 'span',
1776                         cls: 'panel-header-right',
1777                         html: this.rheader
1778                     }
1779                 );
1780                 
1781                 cfg.cn.push({
1782                     cls : 'panel-heading',
1783                     style : this.expandable ? 'cursor: pointer' : '',
1784                     cn : h
1785                 });
1786                 
1787             }
1788             
1789             body = false;
1790             cfg.cn.push({
1791                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1792                 html : this.html
1793             });
1794             
1795             
1796             if (this.footer.length) {
1797                 cfg.cn.push({
1798                     cls : 'panel-footer',
1799                     html : this.footer
1800                     
1801                 });
1802             }
1803             
1804         }
1805         
1806         if (body) {
1807             body.html = this.html || cfg.html;
1808             // prefix with the icons..
1809             if (this.fa) {
1810                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1811             }
1812             if (this.icon) {
1813                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1814             }
1815             
1816             
1817         }
1818         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1819             cfg.cls =  'container';
1820         }
1821         
1822         return cfg;
1823     },
1824     
1825     initEvents: function() 
1826     {
1827         if(this.expandable){
1828             var headerEl = this.headerEl();
1829         
1830             if(headerEl){
1831                 headerEl.on('click', this.onToggleClick, this);
1832             }
1833         }
1834         
1835         if(this.clickable){
1836             this.el.on('click', this.onClick, this);
1837         }
1838         
1839     },
1840     
1841     onToggleClick : function()
1842     {
1843         var headerEl = this.headerEl();
1844         
1845         if(!headerEl){
1846             return;
1847         }
1848         
1849         if(this.expanded){
1850             this.collapse();
1851             return;
1852         }
1853         
1854         this.expand();
1855     },
1856     
1857     expand : function()
1858     {
1859         if(this.fireEvent('expand', this)) {
1860             
1861             this.expanded = true;
1862             
1863             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864             
1865             this.el.select('.panel-body',true).first().removeClass('hide');
1866             
1867             var toggleEl = this.toggleEl();
1868
1869             if(!toggleEl){
1870                 return;
1871             }
1872
1873             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1874         }
1875         
1876     },
1877     
1878     collapse : function()
1879     {
1880         if(this.fireEvent('collapse', this)) {
1881             
1882             this.expanded = false;
1883             
1884             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1885             this.el.select('.panel-body',true).first().addClass('hide');
1886         
1887             var toggleEl = this.toggleEl();
1888
1889             if(!toggleEl){
1890                 return;
1891             }
1892
1893             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1894         }
1895     },
1896     
1897     toggleEl : function()
1898     {
1899         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1900             return;
1901         }
1902         
1903         return this.el.select('.panel-heading .fa',true).first();
1904     },
1905     
1906     headerEl : function()
1907     {
1908         if(!this.el || !this.panel.length || !this.header.length){
1909             return;
1910         }
1911         
1912         return this.el.select('.panel-heading',true).first()
1913     },
1914     
1915     bodyEl : function()
1916     {
1917         if(!this.el || !this.panel.length){
1918             return;
1919         }
1920         
1921         return this.el.select('.panel-body',true).first()
1922     },
1923     
1924     titleEl : function()
1925     {
1926         if(!this.el || !this.panel.length || !this.header.length){
1927             return;
1928         }
1929         
1930         return this.el.select('.panel-title',true).first();
1931     },
1932     
1933     setTitle : function(v)
1934     {
1935         var titleEl = this.titleEl();
1936         
1937         if(!titleEl){
1938             return;
1939         }
1940         
1941         titleEl.dom.innerHTML = v;
1942     },
1943     
1944     getTitle : function()
1945     {
1946         
1947         var titleEl = this.titleEl();
1948         
1949         if(!titleEl){
1950             return '';
1951         }
1952         
1953         return titleEl.dom.innerHTML;
1954     },
1955     
1956     setRightTitle : function(v)
1957     {
1958         var t = this.el.select('.panel-header-right',true).first();
1959         
1960         if(!t){
1961             return;
1962         }
1963         
1964         t.dom.innerHTML = v;
1965     },
1966     
1967     onClick : function(e)
1968     {
1969         e.preventDefault();
1970         
1971         this.fireEvent('click', this, e);
1972     }
1973 });
1974
1975  /**
1976  * @class Roo.bootstrap.Card
1977  * @extends Roo.bootstrap.Component
1978  * @children Roo.bootstrap.Component
1979  * @licence LGPL
1980  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1981  *
1982  *
1983  * possible... may not be implemented..
1984  * @cfg {String} header_image  src url of image.
1985  * @cfg {String|Object} header
1986  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1987  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1988  * 
1989  * @cfg {String} title
1990  * @cfg {String} subtitle
1991  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1992  * @cfg {String} footer
1993  
1994  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995  * 
1996  * @cfg {String} margin (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2002  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003  *
2004  * @cfg {String} padding (0|1|2|3|4|5)
2005  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2006  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2007  * @cfg {String} padding_left (0|1|2|3|4|5)
2008  * @cfg {String} padding_right (0|1|2|3|4|5)
2009  * @cfg {String} padding_x (0|1|2|3|4|5)
2010  * @cfg {String} padding_y (0|1|2|3|4|5)
2011  *
2012  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017  
2018  * @config {Boolean} dragable  if this card can be dragged.
2019  * @config {String} drag_group  group for drag
2020  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2021  * @config {String} drop_group  group for drag
2022  * 
2023  * @config {Boolean} collapsable can the body be collapsed.
2024  * @config {Boolean} collapsed is the body collapsed when rendered...
2025  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2026  * @config {Boolean} rotated is the body rotated when rendered...
2027  * 
2028  * @constructor
2029  * Create a new Container
2030  * @param {Object} config The config object
2031  */
2032
2033 Roo.bootstrap.Card = function(config){
2034     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2035     
2036     this.addEvents({
2037          // raw events
2038         /**
2039          * @event drop
2040          * When a element a card is dropped
2041          * @param {Roo.bootstrap.Card} this
2042          *
2043          * 
2044          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2045          * @param {String} position 'above' or 'below'
2046          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2047         
2048          */
2049         'drop' : true,
2050          /**
2051          * @event rotate
2052          * When a element a card is rotate
2053          * @param {Roo.bootstrap.Card} this
2054          * @param {Roo.Element} n the node being dropped?
2055          * @param {Boolean} rotate status
2056          */
2057         'rotate' : true,
2058         /**
2059          * @event cardover
2060          * When a card element is dragged over ready to drop (return false to block dropable)
2061          * @param {Roo.bootstrap.Card} this
2062          * @param {Object} data from dragdrop 
2063          */
2064          'cardover' : true
2065          
2066     });
2067 };
2068
2069
2070 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2071     
2072     
2073     weight : '',
2074     
2075     margin: '', /// may be better in component?
2076     margin_top: '', 
2077     margin_bottom: '', 
2078     margin_left: '',
2079     margin_right: '',
2080     margin_x: '',
2081     margin_y: '',
2082     
2083     padding : '',
2084     padding_top: '', 
2085     padding_bottom: '', 
2086     padding_left: '',
2087     padding_right: '',
2088     padding_x: '',
2089     padding_y: '',
2090     
2091     display: '', 
2092     display_xs: '', 
2093     display_sm: '', 
2094     display_lg: '',
2095     display_xl: '',
2096  
2097     header_image  : '',
2098     header : '',
2099     header_size : 0,
2100     title : '',
2101     subtitle : '',
2102     html : '',
2103     footer: '',
2104
2105     collapsable : false,
2106     collapsed : false,
2107     rotateable : false,
2108     rotated : false,
2109     
2110     dragable : false,
2111     drag_group : false,
2112     dropable : false,
2113     drop_group : false,
2114     childContainer : false,
2115     dropEl : false, /// the dom placeholde element that indicates drop location.
2116     containerEl: false, // body container
2117     bodyEl: false, // card-body
2118     headerContainerEl : false, //
2119     headerEl : false,
2120     header_imageEl : false,
2121     
2122     
2123     layoutCls : function()
2124     {
2125         var cls = '';
2126         var t = this;
2127         Roo.log(this.margin_bottom.length);
2128         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2129             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130             
2131             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2132                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2133             }
2134             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2135                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2136             }
2137         });
2138         
2139         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2140             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2141                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2142             }
2143         });
2144         
2145         // more generic support?
2146         if (this.hidden) {
2147             cls += ' d-none';
2148         }
2149         
2150         return cls;
2151     },
2152  
2153        // Roo.log("Call onRender: " + this.xtype);
2154         /*  We are looking at something like this.
2155 <div class="card">
2156     <img src="..." class="card-img-top" alt="...">
2157     <div class="card-body">
2158         <h5 class="card-title">Card title</h5>
2159          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160
2161         >> this bit is really the body...
2162         <div> << we will ad dthis in hopefully it will not break shit.
2163         
2164         ** card text does not actually have any styling...
2165         
2166             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2167         
2168         </div> <<
2169           <a href="#" class="card-link">Card link</a>
2170           
2171     </div>
2172     <div class="card-footer">
2173         <small class="text-muted">Last updated 3 mins ago</small>
2174     </div>
2175 </div>
2176          */
2177     getAutoCreate : function(){
2178         
2179         var cfg = {
2180             tag : 'div',
2181             cls : 'card',
2182             cn : [ ]
2183         };
2184         
2185         if (this.weight.length && this.weight != 'light') {
2186             cfg.cls += ' text-white';
2187         } else {
2188             cfg.cls += ' text-dark'; // need as it's nested..
2189         }
2190         if (this.weight.length) {
2191             cfg.cls += ' bg-' + this.weight;
2192         }
2193         
2194         cfg.cls += ' ' + this.layoutCls(); 
2195         
2196         var hdr = false;
2197         var hdr_ctr = false;
2198         if (this.header.length) {
2199             hdr = {
2200                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2201                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202                 cn : []
2203             };
2204             cfg.cn.push(hdr);
2205             hdr_ctr = hdr;
2206         } else {
2207             hdr = {
2208                 tag : 'div',
2209                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2210                 cn : []
2211             };
2212             cfg.cn.push(hdr);
2213             hdr_ctr = hdr;
2214         }
2215         if (this.collapsable) {
2216             hdr_ctr = {
2217             tag : 'a',
2218             cls : 'd-block user-select-none',
2219             cn: [
2220                     {
2221                         tag: 'i',
2222                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2223                     }
2224                    
2225                 ]
2226             };
2227             hdr.cn.push(hdr_ctr);
2228         }
2229         
2230         hdr_ctr.cn.push(        {
2231             tag: 'span',
2232             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2233             html : this.header
2234         });
2235         
2236         
2237         if (this.header_image.length) {
2238             cfg.cn.push({
2239                 tag : 'img',
2240                 cls : 'card-img-top',
2241                 src: this.header_image // escape?
2242             });
2243         } else {
2244             cfg.cn.push({
2245                     tag : 'div',
2246                     cls : 'card-img-top d-none' 
2247                 });
2248         }
2249             
2250         var body = {
2251             tag : 'div',
2252             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2253             cn : []
2254         };
2255         var obody = body;
2256         if (this.collapsable || this.rotateable) {
2257             obody = {
2258                 tag: 'div',
2259                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2260                 cn : [  body ]
2261             };
2262         }
2263         
2264         cfg.cn.push(obody);
2265         
2266         if (this.title.length) {
2267             body.cn.push({
2268                 tag : 'div',
2269                 cls : 'card-title',
2270                 src: this.title // escape?
2271             });
2272         }  
2273         
2274         if (this.subtitle.length) {
2275             body.cn.push({
2276                 tag : 'div',
2277                 cls : 'card-title',
2278                 src: this.subtitle // escape?
2279             });
2280         }
2281         
2282         body.cn.push({
2283             tag : 'div',
2284             cls : 'roo-card-body-ctr'
2285         });
2286         
2287         if (this.html.length) {
2288             body.cn.push({
2289                 tag: 'div',
2290                 html : this.html
2291             });
2292         }
2293         // fixme ? handle objects?
2294         
2295         if (this.footer.length) {
2296            
2297             cfg.cn.push({
2298                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2299                 html : this.footer
2300             });
2301             
2302         } else {
2303             cfg.cn.push({cls : 'card-footer d-none'});
2304         }
2305         
2306         // footer...
2307         
2308         return cfg;
2309     },
2310     
2311     
2312     getCardHeader : function()
2313     {
2314         var  ret = this.el.select('.card-header',true).first();
2315         if (ret.hasClass('d-none')) {
2316             ret.removeClass('d-none');
2317         }
2318         
2319         return ret;
2320     },
2321     getCardFooter : function()
2322     {
2323         var  ret = this.el.select('.card-footer',true).first();
2324         if (ret.hasClass('d-none')) {
2325             ret.removeClass('d-none');
2326         }
2327         
2328         return ret;
2329     },
2330     getCardImageTop : function()
2331     {
2332         var  ret = this.header_imageEl;
2333         if (ret.hasClass('d-none')) {
2334             ret.removeClass('d-none');
2335         }
2336             
2337         return ret;
2338     },
2339     
2340     getChildContainer : function()
2341     {
2342         
2343         if(!this.el){
2344             return false;
2345         }
2346         return this.el.select('.roo-card-body-ctr',true).first();    
2347     },
2348     
2349     initEvents: function() 
2350     {
2351         this.bodyEl = this.el.select('.card-body',true).first(); 
2352         this.containerEl = this.getChildContainer();
2353         if(this.dragable){
2354             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2355                     containerScroll: true,
2356                     ddGroup: this.drag_group || 'default_card_drag_group'
2357             });
2358             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359         }
2360         if (this.dropable) {
2361             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2362                 containerScroll: true,
2363                 ddGroup: this.drop_group || 'default_card_drag_group'
2364             });
2365             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2366             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2367             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2368             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2369             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2370         }
2371         
2372         if (this.collapsable) {
2373             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374         }
2375         if (this.rotateable) {
2376             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377         }
2378         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379          
2380         this.footerEl = this.el.select('.card-footer',true).first();
2381         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2382         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2383         this.headerEl = this.el.select('.card-header',true).first();
2384         
2385         if (this.rotated) {
2386             this.el.addClass('roo-card-rotated');
2387             this.fireEvent('rotate', this, true);
2388         }
2389         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2390         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2391         
2392     },
2393     getDragData : function(e)
2394     {
2395         var target = this.getEl();
2396         if (target) {
2397             //this.handleSelection(e);
2398             
2399             var dragData = {
2400                 source: this,
2401                 copy: false,
2402                 nodes: this.getEl(),
2403                 records: []
2404             };
2405             
2406             
2407             dragData.ddel = target.dom ;    // the div element
2408             Roo.log(target.getWidth( ));
2409             dragData.ddel.style.width = target.getWidth() + 'px';
2410             
2411             return dragData;
2412         }
2413         return false;
2414     },
2415     /**
2416     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2417     *    whole Element becomes the target, and this causes the drop gesture to append.
2418     *
2419     *    Returns an object:
2420     *     {
2421            
2422            position : 'below' or 'above'
2423            card  : relateive to card OBJECT (or true for no cards listed)
2424            items_n : relative to nth item in list
2425            card_n : relative to  nth card in list
2426     }
2427     *
2428     *    
2429     */
2430     getTargetFromEvent : function(e, dragged_card_el)
2431     {
2432         var target = e.getTarget();
2433         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2434             target = target.parentNode;
2435         }
2436         
2437         var ret = {
2438             position: '',
2439             cards : [],
2440             card_n : -1,
2441             items_n : -1,
2442             card : false 
2443         };
2444         
2445         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2446         // see if target is one of the 'cards'...
2447         
2448         
2449         //Roo.log(this.items.length);
2450         var pos = false;
2451         
2452         var last_card_n = 0;
2453         var cards_len  = 0;
2454         for (var i = 0;i< this.items.length;i++) {
2455             
2456             if (!this.items[i].el.hasClass('card')) {
2457                  continue;
2458             }
2459             pos = this.getDropPoint(e, this.items[i].el.dom);
2460             
2461             cards_len = ret.cards.length;
2462             //Roo.log(this.items[i].el.dom.id);
2463             ret.cards.push(this.items[i]);
2464             last_card_n  = i;
2465             if (ret.card_n < 0 && pos == 'above') {
2466                 ret.position = cards_len > 0 ? 'below' : pos;
2467                 ret.items_n = i > 0 ? i - 1 : 0;
2468                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2469                 ret.card = ret.cards[ret.card_n];
2470             }
2471         }
2472         if (!ret.cards.length) {
2473             ret.card = true;
2474             ret.position = 'below';
2475             ret.items_n;
2476             return ret;
2477         }
2478         // could not find a card.. stick it at the end..
2479         if (ret.card_n < 0) {
2480             ret.card_n = last_card_n;
2481             ret.card = ret.cards[last_card_n];
2482             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2483             ret.position = 'below';
2484         }
2485         
2486         if (this.items[ret.items_n].el == dragged_card_el) {
2487             return false;
2488         }
2489         
2490         if (ret.position == 'below') {
2491             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492             
2493             if (card_after  && card_after.el == dragged_card_el) {
2494                 return false;
2495             }
2496             return ret;
2497         }
2498         
2499         // its's after ..
2500         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501         
2502         if (card_before  && card_before.el == dragged_card_el) {
2503             return false;
2504         }
2505         
2506         return ret;
2507     },
2508     
2509     onNodeEnter : function(n, dd, e, data){
2510         return false;
2511     },
2512     onNodeOver : function(n, dd, e, data)
2513     {
2514        
2515         var target_info = this.getTargetFromEvent(e,data.source.el);
2516         if (target_info === false) {
2517             this.dropPlaceHolder('hide');
2518             return false;
2519         }
2520         Roo.log(['getTargetFromEvent', target_info ]);
2521         
2522         
2523         if (this.fireEvent('cardover', this, [ data ]) === false) {
2524             return false;
2525         }
2526         
2527         this.dropPlaceHolder('show', target_info,data);
2528         
2529         return false; 
2530     },
2531     onNodeOut : function(n, dd, e, data){
2532         this.dropPlaceHolder('hide');
2533      
2534     },
2535     onNodeDrop : function(n, dd, e, data)
2536     {
2537         
2538         // call drop - return false if
2539         
2540         // this could actually fail - if the Network drops..
2541         // we will ignore this at present..- client should probably reload
2542         // the whole set of cards if stuff like that fails.
2543         
2544         
2545         var info = this.getTargetFromEvent(e,data.source.el);
2546         if (info === false) {
2547             return false;
2548         }
2549         this.dropPlaceHolder('hide');
2550   
2551           
2552     
2553         this.acceptCard(data.source, info.position, info.card, info.items_n);
2554         return true;
2555          
2556     },
2557     firstChildCard : function()
2558     {
2559         for (var i = 0;i< this.items.length;i++) {
2560             
2561             if (!this.items[i].el.hasClass('card')) {
2562                  continue;
2563             }
2564             return this.items[i];
2565         }
2566         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2567     },
2568     /**
2569      * accept card
2570      *
2571      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2572      */
2573     acceptCard : function(move_card,  position, next_to_card )
2574     {
2575         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2576             return false;
2577         }
2578         
2579         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580         
2581         move_card.parent().removeCard(move_card);
2582         
2583         
2584         var dom = move_card.el.dom;
2585         dom.style.width = ''; // clear with - which is set by drag.
2586         
2587         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2588             var cardel = next_to_card.el.dom;
2589             
2590             if (position == 'above' ) {
2591                 cardel.parentNode.insertBefore(dom, cardel);
2592             } else if (cardel.nextSibling) {
2593                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594             } else {
2595                 cardel.parentNode.append(dom);
2596             }
2597         } else {
2598             // card container???
2599             this.containerEl.dom.append(dom);
2600         }
2601         
2602         //FIXME HANDLE card = true 
2603         
2604         // add this to the correct place in items.
2605         
2606         // remove Card from items.
2607         
2608        
2609         if (this.items.length) {
2610             var nitems = [];
2611             //Roo.log([info.items_n, info.position, this.items.length]);
2612             for (var i =0; i < this.items.length; i++) {
2613                 if (i == to_items_n && position == 'above') {
2614                     nitems.push(move_card);
2615                 }
2616                 nitems.push(this.items[i]);
2617                 if (i == to_items_n && position == 'below') {
2618                     nitems.push(move_card);
2619                 }
2620             }
2621             this.items = nitems;
2622             Roo.log(this.items);
2623         } else {
2624             this.items.push(move_card);
2625         }
2626         
2627         move_card.parentId = this.id;
2628         
2629         return true;
2630         
2631         
2632     },
2633     removeCard : function(c)
2634     {
2635         this.items = this.items.filter(function(e) { return e != c });
2636  
2637         var dom = c.el.dom;
2638         dom.parentNode.removeChild(dom);
2639         dom.style.width = ''; // clear with - which is set by drag.
2640         c.parentId = false;
2641         
2642     },
2643     
2644     /**    Decide whether to drop above or below a View node. */
2645     getDropPoint : function(e, n, dd)
2646     {
2647         if (dd) {
2648              return false;
2649         }
2650         if (n == this.containerEl.dom) {
2651             return "above";
2652         }
2653         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2654         var c = t + (b - t) / 2;
2655         var y = Roo.lib.Event.getPageY(e);
2656         if(y <= c) {
2657             return "above";
2658         }else{
2659             return "below";
2660         }
2661     },
2662     onToggleCollapse : function(e)
2663         {
2664         if (this.collapsed) {
2665             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2666             this.collapsableEl.addClass('show');
2667             this.collapsed = false;
2668             return;
2669         }
2670         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2671         this.collapsableEl.removeClass('show');
2672         this.collapsed = true;
2673         
2674     
2675     },
2676     
2677     onToggleRotate : function(e)
2678     {
2679         this.collapsableEl.removeClass('show');
2680         this.footerEl.removeClass('d-none');
2681         this.el.removeClass('roo-card-rotated');
2682         this.el.removeClass('d-none');
2683         if (this.rotated) {
2684             
2685             this.collapsableEl.addClass('show');
2686             this.rotated = false;
2687             this.fireEvent('rotate', this, this.rotated);
2688             return;
2689         }
2690         this.el.addClass('roo-card-rotated');
2691         this.footerEl.addClass('d-none');
2692         this.el.select('.roo-collapsable').removeClass('show');
2693         
2694         this.rotated = true;
2695         this.fireEvent('rotate', this, this.rotated);
2696     
2697     },
2698     
2699     dropPlaceHolder: function (action, info, data)
2700     {
2701         if (this.dropEl === false) {
2702             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2703             cls : 'd-none'
2704             },true);
2705         }
2706         this.dropEl.removeClass(['d-none', 'd-block']);        
2707         if (action == 'hide') {
2708             
2709             this.dropEl.addClass('d-none');
2710             return;
2711         }
2712         // FIXME - info.card == true!!!
2713         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714         
2715         if (info.card !== true) {
2716             var cardel = info.card.el.dom;
2717             
2718             if (info.position == 'above') {
2719                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2720             } else if (cardel.nextSibling) {
2721                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722             } else {
2723                 cardel.parentNode.append(this.dropEl.dom);
2724             }
2725         } else {
2726             // card container???
2727             this.containerEl.dom.append(this.dropEl.dom);
2728         }
2729         
2730         this.dropEl.addClass('d-block roo-card-dropzone');
2731         
2732         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2733         
2734         
2735     
2736     
2737     
2738     },
2739     setHeaderText: function(html)
2740     {
2741         this.header = html;
2742         if (this.headerContainerEl) {
2743             this.headerContainerEl.dom.innerHTML = html;
2744         }
2745     },
2746     onHeaderImageLoad : function(ev, he)
2747     {
2748         if (!this.header_image_fit_square) {
2749             return;
2750         }
2751         
2752         var hw = he.naturalHeight / he.naturalWidth;
2753         // wide image = < 0
2754         // tall image = > 1
2755         //var w = he.dom.naturalWidth;
2756         var ww = he.width;
2757         he.style.left =  0;
2758         he.style.position =  'relative';
2759         if (hw > 1) {
2760             var nw = (ww * (1/hw));
2761             Roo.get(he).setSize( ww * (1/hw),  ww);
2762             he.style.left =  ((ww - nw)/ 2) + 'px';
2763             he.style.position =  'relative';
2764         }
2765
2766     }
2767
2768     
2769 });
2770
2771 /*
2772  * - LGPL
2773  *
2774  * Card header - holder for the card header elements.
2775  * 
2776  */
2777
2778 /**
2779  * @class Roo.bootstrap.CardHeader
2780  * @extends Roo.bootstrap.Element
2781  * @parent Roo.bootstrap.Card
2782  * @children Roo.bootstrap.Component
2783  * Bootstrap CardHeader class
2784  * @constructor
2785  * Create a new Card Header - that you can embed children into
2786  * @param {Object} config The config object
2787  */
2788
2789 Roo.bootstrap.CardHeader = function(config){
2790     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2791 };
2792
2793 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2794     
2795     
2796     container_method : 'getCardHeader' 
2797     
2798      
2799     
2800     
2801    
2802 });
2803
2804  
2805
2806  /*
2807  * - LGPL
2808  *
2809  * Card footer - holder for the card footer elements.
2810  * 
2811  */
2812
2813 /**
2814  * @class Roo.bootstrap.CardFooter
2815  * @extends Roo.bootstrap.Element
2816  * @parent Roo.bootstrap.Card
2817  * @children Roo.bootstrap.Component
2818  * Bootstrap CardFooter class
2819  * 
2820  * @constructor
2821  * Create a new Card Footer - that you can embed children into
2822  * @param {Object} config The config object
2823  */
2824
2825 Roo.bootstrap.CardFooter = function(config){
2826     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2827 };
2828
2829 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2830     
2831     
2832     container_method : 'getCardFooter' 
2833     
2834      
2835     
2836     
2837    
2838 });
2839
2840  
2841
2842  /*
2843  * - LGPL
2844  *
2845  * Card header - holder for the card header elements.
2846  * 
2847  */
2848
2849 /**
2850  * @class Roo.bootstrap.CardImageTop
2851  * @extends Roo.bootstrap.Element
2852  * @parent Roo.bootstrap.Card
2853  * @children Roo.bootstrap.Component
2854  * Bootstrap CardImageTop class
2855  * 
2856  * @constructor
2857  * Create a new Card Image Top container
2858  * @param {Object} config The config object
2859  */
2860
2861 Roo.bootstrap.CardImageTop = function(config){
2862     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2863 };
2864
2865 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2866     
2867    
2868     container_method : 'getCardImageTop' 
2869     
2870      
2871     
2872    
2873 });
2874
2875  
2876
2877  
2878 /*
2879 * Licence: LGPL
2880 */
2881
2882 /**
2883  * @class Roo.bootstrap.ButtonUploader
2884  * @extends Roo.bootstrap.Button
2885  * Bootstrap Button Uploader class - it's a button which when you add files to it
2886  *
2887  * 
2888  * @cfg {Number} errorTimeout default 3000
2889  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2890  * @cfg {Array}  html The button text.
2891  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2892  *
2893  * @constructor
2894  * Create a new CardUploader
2895  * @param {Object} config The config object
2896  */
2897
2898 Roo.bootstrap.ButtonUploader = function(config){
2899     
2900  
2901     
2902     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2903     
2904      
2905      this.addEvents({
2906          // raw events
2907         /**
2908          * @event beforeselect
2909          * When button is pressed, before show upload files dialog is shown
2910          * @param {Roo.bootstrap.UploaderButton} this
2911          *
2912          */
2913         'beforeselect' : true,
2914          /**
2915          * @event fired when files have been selected, 
2916          * When a the download link is clicked
2917          * @param {Roo.bootstrap.UploaderButton} this
2918          * @param {Array} Array of files that have been uploaded
2919          */
2920         'uploaded' : true
2921         
2922     });
2923 };
2924  
2925 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2926     
2927      
2928     errorTimeout : 3000,
2929      
2930     images : false,
2931    
2932     fileCollection : false,
2933     allowBlank : true,
2934     
2935     multiple : true,
2936     
2937     getAutoCreate : function()
2938     {
2939         var im = {
2940             tag: 'input',
2941             type : 'file',
2942             cls : 'd-none  roo-card-upload-selector' 
2943           
2944         };
2945         if (this.multiple) {
2946             im.multiple = 'multiple';
2947         }
2948         
2949         return  {
2950             cls :'div' ,
2951             cn : [
2952                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2953                 im
2954
2955             ]
2956         };
2957            
2958          
2959     },
2960      
2961    
2962     initEvents : function()
2963     {
2964         
2965         Roo.bootstrap.Button.prototype.initEvents.call(this);
2966         
2967         
2968         
2969         
2970         
2971         this.urlAPI = (window.createObjectURL && window) || 
2972                                 (window.URL && URL.revokeObjectURL && URL) || 
2973                                 (window.webkitURL && webkitURL);
2974                         
2975          
2976          
2977          
2978         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2979         
2980         this.selectorEl.on('change', this.onFileSelected, this);
2981          
2982          
2983        
2984     },
2985     
2986    
2987     onClick : function(e)
2988     {
2989         e.preventDefault();
2990         
2991         if ( this.fireEvent('beforeselect', this) === false) {
2992             return;
2993         }
2994          
2995         this.selectorEl.dom.click();
2996          
2997     },
2998     
2999     onFileSelected : function(e)
3000     {
3001         e.preventDefault();
3002         
3003         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3004             return;
3005         }
3006         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3007         this.selectorEl.dom.value  = '';// hopefully reset..
3008         
3009         this.fireEvent('uploaded', this,  files );
3010         
3011     },
3012     
3013        
3014    
3015     
3016     /**
3017      * addCard - add an Attachment to the uploader
3018      * @param data - the data about the image to upload
3019      *
3020      * {
3021           id : 123
3022           title : "Title of file",
3023           is_uploaded : false,
3024           src : "http://.....",
3025           srcfile : { the File upload object },
3026           mimetype : file.type,
3027           preview : false,
3028           is_deleted : 0
3029           .. any other data...
3030         }
3031      *
3032      * 
3033     */
3034      
3035     reset: function()
3036     {
3037          
3038          this.selectorEl
3039     } 
3040     
3041     
3042     
3043     
3044 });
3045  /*
3046  * - LGPL
3047  *
3048  * image
3049  * 
3050  */
3051
3052
3053 /**
3054  * @class Roo.bootstrap.Img
3055  * @extends Roo.bootstrap.Component
3056  * Bootstrap Img class
3057  * @cfg {Boolean} imgResponsive false | true
3058  * @cfg {String} border rounded | circle | thumbnail
3059  * @cfg {String} src image source
3060  * @cfg {String} alt image alternative text
3061  * @cfg {String} href a tag href
3062  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3063  * @cfg {String} xsUrl xs image source
3064  * @cfg {String} smUrl sm image source
3065  * @cfg {String} mdUrl md image source
3066  * @cfg {String} lgUrl lg image source
3067  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3068  * 
3069  * @constructor
3070  * Create a new Input
3071  * @param {Object} config The config object
3072  */
3073
3074 Roo.bootstrap.Img = function(config){
3075     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3076     
3077     this.addEvents({
3078         // img events
3079         /**
3080          * @event click
3081          * The img click event for the img.
3082          * @param {Roo.EventObject} e
3083          */
3084         "click" : true,
3085         /**
3086          * @event load
3087          * The when any image loads
3088          * @param {Roo.EventObject} e
3089          */
3090         "load" : true
3091     });
3092 };
3093
3094 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3095     
3096     imgResponsive: true,
3097     border: '',
3098     src: 'about:blank',
3099     href: false,
3100     target: false,
3101     xsUrl: '',
3102     smUrl: '',
3103     mdUrl: '',
3104     lgUrl: '',
3105     backgroundContain : false,
3106
3107     getAutoCreate : function()
3108     {   
3109         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3110             return this.createSingleImg();
3111         }
3112         
3113         var cfg = {
3114             tag: 'div',
3115             cls: 'roo-image-responsive-group',
3116             cn: []
3117         };
3118         var _this = this;
3119         
3120         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3121             
3122             if(!_this[size + 'Url']){
3123                 return;
3124             }
3125             
3126             var img = {
3127                 tag: 'img',
3128                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3129                 html: _this.html || cfg.html,
3130                 src: _this[size + 'Url']
3131             };
3132             
3133             img.cls += ' roo-image-responsive-' + size;
3134             
3135             var s = ['xs', 'sm', 'md', 'lg'];
3136             
3137             s.splice(s.indexOf(size), 1);
3138             
3139             Roo.each(s, function(ss){
3140                 img.cls += ' hidden-' + ss;
3141             });
3142             
3143             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3144                 cfg.cls += ' img-' + _this.border;
3145             }
3146             
3147             if(_this.alt){
3148                 cfg.alt = _this.alt;
3149             }
3150             
3151             if(_this.href){
3152                 var a = {
3153                     tag: 'a',
3154                     href: _this.href,
3155                     cn: [
3156                         img
3157                     ]
3158                 };
3159
3160                 if(this.target){
3161                     a.target = _this.target;
3162                 }
3163             }
3164             
3165             cfg.cn.push((_this.href) ? a : img);
3166             
3167         });
3168         
3169         return cfg;
3170     },
3171     
3172     createSingleImg : function()
3173     {
3174         var cfg = {
3175             tag: 'img',
3176             cls: (this.imgResponsive) ? 'img-responsive' : '',
3177             html : null,
3178             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3179         };
3180         
3181         if (this.backgroundContain) {
3182             cfg.cls += ' background-contain';
3183         }
3184         
3185         cfg.html = this.html || cfg.html;
3186         
3187         if (this.backgroundContain) {
3188             cfg.style="background-image: url(" + this.src + ')';
3189         } else {
3190             cfg.src = this.src || cfg.src;
3191         }
3192         
3193         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3194             cfg.cls += ' img-' + this.border;
3195         }
3196         
3197         if(this.alt){
3198             cfg.alt = this.alt;
3199         }
3200         
3201         if(this.href){
3202             var a = {
3203                 tag: 'a',
3204                 href: this.href,
3205                 cn: [
3206                     cfg
3207                 ]
3208             };
3209             
3210             if(this.target){
3211                 a.target = this.target;
3212             }
3213             
3214         }
3215         
3216         return (this.href) ? a : cfg;
3217     },
3218     
3219     initEvents: function() 
3220     {
3221         if(!this.href){
3222             this.el.on('click', this.onClick, this);
3223         }
3224         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3225             this.el.on('load', this.onImageLoad, this);
3226         } else {
3227             // not sure if this works.. not tested
3228             this.el.select('img', true).on('load', this.onImageLoad, this);
3229         }
3230         
3231     },
3232     
3233     onClick : function(e)
3234     {
3235         Roo.log('img onclick');
3236         this.fireEvent('click', this, e);
3237     },
3238     onImageLoad: function(e)
3239     {
3240         Roo.log('img load');
3241         this.fireEvent('load', this, e);
3242     },
3243     
3244     /**
3245      * Sets the url of the image - used to update it
3246      * @param {String} url the url of the image
3247      */
3248     
3249     setSrc : function(url)
3250     {
3251         this.src =  url;
3252         
3253         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3254             if (this.backgroundContain) {
3255                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3256             } else {
3257                 this.el.dom.src =  url;
3258             }
3259             return;
3260         }
3261         
3262         this.el.select('img', true).first().dom.src =  url;
3263     }
3264     
3265     
3266    
3267 });
3268
3269  /*
3270  * - LGPL
3271  *
3272  * image
3273  * 
3274  */
3275
3276
3277 /**
3278  * @class Roo.bootstrap.Link
3279  * @extends Roo.bootstrap.Component
3280  * @children Roo.bootstrap.Component
3281  * Bootstrap Link Class (eg. '<a href>')
3282  
3283  * @cfg {String} alt image alternative text
3284  * @cfg {String} href a tag href
3285  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3286  * @cfg {String} html the content of the link.
3287  * @cfg {String} anchor name for the anchor link
3288  * @cfg {String} fa - favicon
3289
3290  * @cfg {Boolean} preventDefault (true | false) default false
3291
3292  * 
3293  * @constructor
3294  * Create a new Input
3295  * @param {Object} config The config object
3296  */
3297
3298 Roo.bootstrap.Link = function(config){
3299     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3300     
3301     this.addEvents({
3302         // img events
3303         /**
3304          * @event click
3305          * The img click event for the img.
3306          * @param {Roo.EventObject} e
3307          */
3308         "click" : true
3309     });
3310 };
3311
3312 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3313     
3314     href: false,
3315     target: false,
3316     preventDefault: false,
3317     anchor : false,
3318     alt : false,
3319     fa: false,
3320
3321
3322     getAutoCreate : function()
3323     {
3324         var html = this.html || '';
3325         
3326         if (this.fa !== false) {
3327             html = '<i class="fa fa-' + this.fa + '"></i>';
3328         }
3329         var cfg = {
3330             tag: 'a'
3331         };
3332         // anchor's do not require html/href...
3333         if (this.anchor === false) {
3334             cfg.html = html;
3335             cfg.href = this.href || '#';
3336         } else {
3337             cfg.name = this.anchor;
3338             if (this.html !== false || this.fa !== false) {
3339                 cfg.html = html;
3340             }
3341             if (this.href !== false) {
3342                 cfg.href = this.href;
3343             }
3344         }
3345         
3346         if(this.alt !== false){
3347             cfg.alt = this.alt;
3348         }
3349         
3350         
3351         if(this.target !== false) {
3352             cfg.target = this.target;
3353         }
3354         
3355         return cfg;
3356     },
3357     
3358     initEvents: function() {
3359         
3360         if(!this.href || this.preventDefault){
3361             this.el.on('click', this.onClick, this);
3362         }
3363     },
3364     
3365     onClick : function(e)
3366     {
3367         if(this.preventDefault){
3368             e.preventDefault();
3369         }
3370         //Roo.log('img onclick');
3371         this.fireEvent('click', this, e);
3372     }
3373    
3374 });
3375
3376  /*
3377  * - LGPL
3378  *
3379  * header
3380  * 
3381  */
3382
3383 /**
3384  * @class Roo.bootstrap.Header
3385  * @extends Roo.bootstrap.Component
3386  * @children Roo.bootstrap.Component
3387  * Bootstrap Header class
3388  *
3389  * 
3390  * @cfg {String} html content of header
3391  * @cfg {Number} level (1|2|3|4|5|6) default 1
3392  * 
3393  * @constructor
3394  * Create a new Header
3395  * @param {Object} config The config object
3396  */
3397
3398
3399 Roo.bootstrap.Header  = function(config){
3400     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3401 };
3402
3403 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3404     
3405     //href : false,
3406     html : false,
3407     level : 1,
3408     
3409     
3410     
3411     getAutoCreate : function(){
3412         
3413         
3414         
3415         var cfg = {
3416             tag: 'h' + (1 *this.level),
3417             html: this.html || ''
3418         } ;
3419         
3420         return cfg;
3421     }
3422    
3423 });
3424
3425  
3426
3427  /**
3428  * @class Roo.bootstrap.MenuMgr
3429  * @licence LGPL
3430  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3431  * @static
3432  */
3433 Roo.bootstrap.menu.Manager = function(){
3434    var menus, active, groups = {}, attached = false, lastShow = new Date();
3435
3436    // private - called when first menu is created
3437    function init(){
3438        menus = {};
3439        active = new Roo.util.MixedCollection();
3440        Roo.get(document).addKeyListener(27, function(){
3441            if(active.length > 0){
3442                hideAll();
3443            }
3444        });
3445    }
3446
3447    // private
3448    function hideAll(){
3449        if(active && active.length > 0){
3450            var c = active.clone();
3451            c.each(function(m){
3452                m.hide();
3453            });
3454        }
3455    }
3456
3457    // private
3458    function onHide(m){
3459        active.remove(m);
3460        if(active.length < 1){
3461            Roo.get(document).un("mouseup", onMouseDown);
3462             
3463            attached = false;
3464        }
3465    }
3466
3467    // private
3468    function onShow(m){
3469        var last = active.last();
3470        lastShow = new Date();
3471        active.add(m);
3472        if(!attached){
3473           Roo.get(document).on("mouseup", onMouseDown);
3474            
3475            attached = true;
3476        }
3477        if(m.parentMenu){
3478           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3479           m.parentMenu.activeChild = m;
3480        }else if(last && last.isVisible()){
3481           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3482        }
3483    }
3484
3485    // private
3486    function onBeforeHide(m){
3487        if(m.activeChild){
3488            m.activeChild.hide();
3489        }
3490        if(m.autoHideTimer){
3491            clearTimeout(m.autoHideTimer);
3492            delete m.autoHideTimer;
3493        }
3494    }
3495
3496    // private
3497    function onBeforeShow(m){
3498        var pm = m.parentMenu;
3499        if(!pm && !m.allowOtherMenus){
3500            hideAll();
3501        }else if(pm && pm.activeChild && active != m){
3502            pm.activeChild.hide();
3503        }
3504    }
3505
3506    // private this should really trigger on mouseup..
3507    function onMouseDown(e){
3508         Roo.log("on Mouse Up");
3509         
3510         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3511             Roo.log("MenuManager hideAll");
3512             hideAll();
3513             e.stopEvent();
3514         }
3515         
3516         
3517    }
3518
3519    // private
3520    function onBeforeCheck(mi, state){
3521        if(state){
3522            var g = groups[mi.group];
3523            for(var i = 0, l = g.length; i < l; i++){
3524                if(g[i] != mi){
3525                    g[i].setChecked(false);
3526                }
3527            }
3528        }
3529    }
3530
3531    return {
3532
3533        /**
3534         * Hides all menus that are currently visible
3535         */
3536        hideAll : function(){
3537             hideAll();  
3538        },
3539
3540        // private
3541        register : function(menu){
3542            if(!menus){
3543                init();
3544            }
3545            menus[menu.id] = menu;
3546            menu.on("beforehide", onBeforeHide);
3547            menu.on("hide", onHide);
3548            menu.on("beforeshow", onBeforeShow);
3549            menu.on("show", onShow);
3550            var g = menu.group;
3551            if(g && menu.events["checkchange"]){
3552                if(!groups[g]){
3553                    groups[g] = [];
3554                }
3555                groups[g].push(menu);
3556                menu.on("checkchange", onCheck);
3557            }
3558        },
3559
3560         /**
3561          * Returns a {@link Roo.menu.Menu} object
3562          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3563          * be used to generate and return a new Menu instance.
3564          */
3565        get : function(menu){
3566            if(typeof menu == "string"){ // menu id
3567                return menus[menu];
3568            }else if(menu.events){  // menu instance
3569                return menu;
3570            }
3571            /*else if(typeof menu.length == 'number'){ // array of menu items?
3572                return new Roo.bootstrap.Menu({items:menu});
3573            }else{ // otherwise, must be a config
3574                return new Roo.bootstrap.Menu(menu);
3575            }
3576            */
3577            return false;
3578        },
3579
3580        // private
3581        unregister : function(menu){
3582            delete menus[menu.id];
3583            menu.un("beforehide", onBeforeHide);
3584            menu.un("hide", onHide);
3585            menu.un("beforeshow", onBeforeShow);
3586            menu.un("show", onShow);
3587            var g = menu.group;
3588            if(g && menu.events["checkchange"]){
3589                groups[g].remove(menu);
3590                menu.un("checkchange", onCheck);
3591            }
3592        },
3593
3594        // private
3595        registerCheckable : function(menuItem){
3596            var g = menuItem.group;
3597            if(g){
3598                if(!groups[g]){
3599                    groups[g] = [];
3600                }
3601                groups[g].push(menuItem);
3602                menuItem.on("beforecheckchange", onBeforeCheck);
3603            }
3604        },
3605
3606        // private
3607        unregisterCheckable : function(menuItem){
3608            var g = menuItem.group;
3609            if(g){
3610                groups[g].remove(menuItem);
3611                menuItem.un("beforecheckchange", onBeforeCheck);
3612            }
3613        }
3614    };
3615 }(); 
3616 /**
3617  * @class Roo.bootstrap.menu.Menu
3618  * @extends Roo.bootstrap.Component
3619  * @licence LGPL
3620  * @children Roo.bootstrap.menu.Item
3621  * @parent none
3622  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3623  * 
3624  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3625  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3626  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3627  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3628   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3629   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3630  
3631  * @constructor
3632  * Create a new Menu
3633  * @param {Object} config The config objectQ
3634  */
3635
3636
3637 Roo.bootstrap.menu.Menu = function(config){
3638     
3639     if (config.type == 'treeview') {
3640         // normally menu's are drawn attached to the document to handle layering etc..
3641         // however treeview (used by the docs menu is drawn into the parent element)
3642         this.container_method = 'getChildContainer'; 
3643     }
3644     
3645     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3646     if (this.registerMenu && this.type != 'treeview')  {
3647         Roo.bootstrap.menu.Manager.register(this);
3648     }
3649     
3650     
3651     this.addEvents({
3652         /**
3653          * @event beforeshow
3654          * Fires before this menu is displayed (return false to block)
3655          * @param {Roo.menu.Menu} this
3656          */
3657         beforeshow : true,
3658         /**
3659          * @event beforehide
3660          * Fires before this menu is hidden (return false to block)
3661          * @param {Roo.menu.Menu} this
3662          */
3663         beforehide : true,
3664         /**
3665          * @event show
3666          * Fires after this menu is displayed
3667          * @param {Roo.menu.Menu} this
3668          */
3669         show : true,
3670         /**
3671          * @event hide
3672          * Fires after this menu is hidden
3673          * @param {Roo.menu.Menu} this
3674          */
3675         hide : true,
3676         /**
3677          * @event click
3678          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3679          * @param {Roo.menu.Menu} this
3680          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3681          * @param {Roo.EventObject} e
3682          */
3683         click : true,
3684         /**
3685          * @event mouseover
3686          * Fires when the mouse is hovering over this menu
3687          * @param {Roo.menu.Menu} this
3688          * @param {Roo.EventObject} e
3689          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3690          */
3691         mouseover : true,
3692         /**
3693          * @event mouseout
3694          * Fires when the mouse exits this menu
3695          * @param {Roo.menu.Menu} this
3696          * @param {Roo.EventObject} e
3697          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3698          */
3699         mouseout : true,
3700         /**
3701          * @event itemclick
3702          * Fires when a menu item contained in this menu is clicked
3703          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3704          * @param {Roo.EventObject} e
3705          */
3706         itemclick: true
3707     });
3708     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3709 };
3710
3711 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3712     
3713    /// html : false,
3714    
3715     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3716     type: false,
3717     /**
3718      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719      */
3720     registerMenu : true,
3721     
3722     menuItems :false, // stores the menu items..
3723     
3724     hidden:true,
3725         
3726     parentMenu : false,
3727     
3728     stopEvent : true,
3729     
3730     isLink : false,
3731     
3732     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733     
3734     hideTrigger : false,
3735     
3736     align : 'tl-bl?',
3737     
3738     
3739     getChildContainer : function() {
3740         return this.el;  
3741     },
3742     
3743     getAutoCreate : function(){
3744          
3745         //if (['right'].indexOf(this.align)!==-1) {
3746         //    cfg.cn[1].cls += ' pull-right'
3747         //}
3748          
3749         var cfg = {
3750             tag : 'ul',
3751             cls : 'dropdown-menu shadow' ,
3752             style : 'z-index:1000'
3753             
3754         };
3755         
3756         if (this.type === 'submenu') {
3757             cfg.cls = 'submenu active';
3758         }
3759         if (this.type === 'treeview') {
3760             cfg.cls = 'treeview-menu';
3761         }
3762         
3763         return cfg;
3764     },
3765     initEvents : function() {
3766         
3767        // Roo.log("ADD event");
3768        // Roo.log(this.triggerEl.dom);
3769         if (this.triggerEl) {
3770             
3771             this.triggerEl.on('click', this.onTriggerClick, this);
3772             
3773             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774             
3775             if (!this.hideTrigger) {
3776                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3777                     // dropdown toggle on the 'a' in BS4?
3778                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779                 } else {
3780                     this.triggerEl.addClass('dropdown-toggle');
3781                 }
3782             }
3783         }
3784         
3785         if (Roo.isTouch) {
3786             this.el.on('touchstart'  , this.onTouch, this);
3787         }
3788         this.el.on('click' , this.onClick, this);
3789
3790         this.el.on("mouseover", this.onMouseOver, this);
3791         this.el.on("mouseout", this.onMouseOut, this);
3792         
3793     },
3794     
3795     findTargetItem : function(e)
3796     {
3797         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3798         if(!t){
3799             return false;
3800         }
3801         //Roo.log(t);         Roo.log(t.id);
3802         if(t && t.id){
3803             //Roo.log(this.menuitems);
3804             return this.menuitems.get(t.id);
3805             
3806             //return this.items.get(t.menuItemId);
3807         }
3808         
3809         return false;
3810     },
3811     
3812     onTouch : function(e) 
3813     {
3814         Roo.log("menu.onTouch");
3815         //e.stopEvent(); this make the user popdown broken
3816         this.onClick(e);
3817     },
3818     
3819     onClick : function(e)
3820     {
3821         Roo.log("menu.onClick");
3822         
3823         var t = this.findTargetItem(e);
3824         if(!t || t.isContainer){
3825             return;
3826         }
3827         Roo.log(e);
3828         /*
3829         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3830             if(t == this.activeItem && t.shouldDeactivate(e)){
3831                 this.activeItem.deactivate();
3832                 delete this.activeItem;
3833                 return;
3834             }
3835             if(t.canActivate){
3836                 this.setActiveItem(t, true);
3837             }
3838             return;
3839             
3840             
3841         }
3842         */
3843        
3844         Roo.log('pass click event');
3845         
3846         t.onClick(e);
3847         
3848         this.fireEvent("click", this, t, e);
3849         
3850         var _this = this;
3851         
3852         if(!t.href.length || t.href == '#'){
3853             (function() { _this.hide(); }).defer(100);
3854         }
3855         
3856     },
3857     
3858     onMouseOver : function(e){
3859         var t  = this.findTargetItem(e);
3860         //Roo.log(t);
3861         //if(t){
3862         //    if(t.canActivate && !t.disabled){
3863         //        this.setActiveItem(t, true);
3864         //    }
3865         //}
3866         
3867         this.fireEvent("mouseover", this, e, t);
3868     },
3869     isVisible : function(){
3870         return !this.hidden;
3871     },
3872     onMouseOut : function(e){
3873         var t  = this.findTargetItem(e);
3874         
3875         //if(t ){
3876         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3877         //        this.activeItem.deactivate();
3878         //        delete this.activeItem;
3879         //    }
3880         //}
3881         this.fireEvent("mouseout", this, e, t);
3882     },
3883     
3884     
3885     /**
3886      * Displays this menu relative to another element
3887      * @param {String/HTMLElement/Roo.Element} element The element to align to
3888      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3889      * the element (defaults to this.defaultAlign)
3890      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891      */
3892     show : function(el, pos, parentMenu)
3893     {
3894         if (false === this.fireEvent("beforeshow", this)) {
3895             Roo.log("show canceled");
3896             return;
3897         }
3898         this.parentMenu = parentMenu;
3899         if(!this.el){
3900             this.render();
3901         }
3902         this.el.addClass('show'); // show otherwise we do not know how big we are..
3903          
3904         var xy = this.el.getAlignToXY(el, pos);
3905         
3906         // bl-tl << left align  below
3907         // tl-bl << left align 
3908         
3909         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3910             // if it goes to far to the right.. -> align left.
3911             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3912         }
3913         if(xy[0] < 0){
3914             // was left align - go right?
3915             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3916         }
3917         
3918         // goes down the bottom
3919         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920            xy[1]  < 0 ){
3921             var a = this.align.replace('?', '').split('-');
3922             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3923             
3924         }
3925         
3926         this.showAt(  xy , parentMenu, false);
3927     },
3928      /**
3929      * Displays this menu at a specific xy position
3930      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3931      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932      */
3933     showAt : function(xy, parentMenu, /* private: */_e){
3934         this.parentMenu = parentMenu;
3935         if(!this.el){
3936             this.render();
3937         }
3938         if(_e !== false){
3939             this.fireEvent("beforeshow", this);
3940             //xy = this.el.adjustForConstraints(xy);
3941         }
3942         
3943         //this.el.show();
3944         this.hideMenuItems();
3945         this.hidden = false;
3946         if (this.triggerEl) {
3947             this.triggerEl.addClass('open');
3948         }
3949         
3950         this.el.addClass('show');
3951         
3952         
3953         
3954         // reassign x when hitting right
3955         
3956         // reassign y when hitting bottom
3957         
3958         // but the list may align on trigger left or trigger top... should it be a properity?
3959         
3960         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3961             this.el.setXY(xy);
3962         }
3963         
3964         this.focus();
3965         this.fireEvent("show", this);
3966     },
3967     
3968     focus : function(){
3969         return;
3970         if(!this.hidden){
3971             this.doFocus.defer(50, this);
3972         }
3973     },
3974
3975     doFocus : function(){
3976         if(!this.hidden){
3977             this.focusEl.focus();
3978         }
3979     },
3980
3981     /**
3982      * Hides this menu and optionally all parent menus
3983      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984      */
3985     hide : function(deep)
3986     {
3987         if (false === this.fireEvent("beforehide", this)) {
3988             Roo.log("hide canceled");
3989             return;
3990         }
3991         this.hideMenuItems();
3992         if(this.el && this.isVisible()){
3993            
3994             if(this.activeItem){
3995                 this.activeItem.deactivate();
3996                 this.activeItem = null;
3997             }
3998             if (this.triggerEl) {
3999                 this.triggerEl.removeClass('open');
4000             }
4001             
4002             this.el.removeClass('show');
4003             this.hidden = true;
4004             this.fireEvent("hide", this);
4005         }
4006         if(deep === true && this.parentMenu){
4007             this.parentMenu.hide(true);
4008         }
4009     },
4010     
4011     onTriggerClick : function(e)
4012     {
4013         Roo.log('trigger click');
4014         
4015         var target = e.getTarget();
4016         
4017         Roo.log(target.nodeName.toLowerCase());
4018         
4019         if(target.nodeName.toLowerCase() === 'i'){
4020             e.preventDefault();
4021         }
4022         
4023     },
4024     
4025     onTriggerPress  : function(e)
4026     {
4027         Roo.log('trigger press');
4028         //Roo.log(e.getTarget());
4029        // Roo.log(this.triggerEl.dom);
4030        
4031         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4032         var pel = Roo.get(e.getTarget());
4033         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4034             Roo.log('is treeview or dropdown?');
4035             return;
4036         }
4037         
4038         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039             return;
4040         }
4041         
4042         if (this.isVisible()) {
4043             Roo.log('hide');
4044             this.hide();
4045         } else {
4046             Roo.log('show');
4047             
4048             this.show(this.triggerEl, this.align, false);
4049         }
4050         
4051         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4052             e.stopEvent();
4053         }
4054         
4055     },
4056        
4057     
4058     hideMenuItems : function()
4059     {
4060         Roo.log("hide Menu Items");
4061         if (!this.el) { 
4062             return;
4063         }
4064         
4065         this.el.select('.open',true).each(function(aa) {
4066             
4067             aa.removeClass('open');
4068          
4069         });
4070     },
4071     addxtypeChild : function (tree, cntr) {
4072         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073           
4074         this.menuitems.add(comp);
4075         return comp;
4076
4077     },
4078     getEl : function()
4079     {
4080         Roo.log(this.el);
4081         return this.el;
4082     },
4083     
4084     clear : function()
4085     {
4086         this.getEl().dom.innerHTML = '';
4087         this.menuitems.clear();
4088     }
4089 });
4090
4091  
4092  /**
4093  * @class Roo.bootstrap.menu.Item
4094  * @extends Roo.bootstrap.Component
4095  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4096  * @parent Roo.bootstrap.menu.Menu
4097  * @licence LGPL
4098  * Bootstrap MenuItem class
4099  * 
4100  * @cfg {String} html the menu label
4101  * @cfg {String} href the link
4102  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4103  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4104  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4105  * @cfg {String} fa favicon to show on left of menu item.
4106  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107  * 
4108  * 
4109  * @constructor
4110  * Create a new MenuItem
4111  * @param {Object} config The config object
4112  */
4113
4114
4115 Roo.bootstrap.menu.Item = function(config){
4116     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4117     this.addEvents({
4118         // raw events
4119         /**
4120          * @event click
4121          * The raw click event for the entire grid.
4122          * @param {Roo.bootstrap.menu.Item} this
4123          * @param {Roo.EventObject} e
4124          */
4125         "click" : true
4126     });
4127 };
4128
4129 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4130     
4131     href : false,
4132     html : false,
4133     preventDefault: false,
4134     isContainer : false,
4135     active : false,
4136     fa: false,
4137     
4138     getAutoCreate : function(){
4139         
4140         if(this.isContainer){
4141             return {
4142                 tag: 'li',
4143                 cls: 'dropdown-menu-item '
4144             };
4145         }
4146         var ctag = {
4147             tag: 'span',
4148             html: 'Link'
4149         };
4150         
4151         var anc = {
4152             tag : 'a',
4153             cls : 'dropdown-item',
4154             href : '#',
4155             cn : [  ]
4156         };
4157         
4158         if (this.fa !== false) {
4159             anc.cn.push({
4160                 tag : 'i',
4161                 cls : 'fa fa-' + this.fa
4162             });
4163         }
4164         
4165         anc.cn.push(ctag);
4166         
4167         
4168         var cfg= {
4169             tag: 'li',
4170             cls: 'dropdown-menu-item',
4171             cn: [ anc ]
4172         };
4173         if (this.parent().type == 'treeview') {
4174             cfg.cls = 'treeview-menu';
4175         }
4176         if (this.active) {
4177             cfg.cls += ' active';
4178         }
4179         
4180         
4181         
4182         anc.href = this.href || cfg.cn[0].href ;
4183         ctag.html = this.html || cfg.cn[0].html ;
4184         return cfg;
4185     },
4186     
4187     initEvents: function()
4188     {
4189         if (this.parent().type == 'treeview') {
4190             this.el.select('a').on('click', this.onClick, this);
4191         }
4192         
4193         if (this.menu) {
4194             this.menu.parentType = this.xtype;
4195             this.menu.triggerEl = this.el;
4196             this.menu = this.addxtype(Roo.apply({}, this.menu));
4197         }
4198         
4199     },
4200     onClick : function(e)
4201     {
4202         Roo.log('item on click ');
4203         
4204         if(this.preventDefault){
4205             e.preventDefault();
4206         }
4207         //this.parent().hideMenuItems();
4208         
4209         this.fireEvent('click', this, e);
4210     },
4211     getEl : function()
4212     {
4213         return this.el;
4214     } 
4215 });
4216
4217  
4218
4219  
4220
4221   
4222 /**
4223  * @class Roo.bootstrap.menu.Separator
4224  * @extends Roo.bootstrap.Component
4225  * @licence LGPL
4226  * @parent Roo.bootstrap.menu.Menu
4227  * Bootstrap Separator class
4228  * 
4229  * @constructor
4230  * Create a new Separator
4231  * @param {Object} config The config object
4232  */
4233
4234
4235 Roo.bootstrap.menu.Separator = function(config){
4236     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4237 };
4238
4239 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4240     
4241     getAutoCreate : function(){
4242         var cfg = {
4243             tag : 'li',
4244             cls: 'dropdown-divider divider'
4245         };
4246         
4247         return cfg;
4248     }
4249    
4250 });
4251
4252  
4253
4254  
4255 /*
4256 * Licence: LGPL
4257 */
4258
4259 /**
4260  * @class Roo.bootstrap.Modal
4261  * @extends Roo.bootstrap.Component
4262  * @builder-top
4263  * @parent none
4264  * @children Roo.bootstrap.Component
4265  * Bootstrap Modal class
4266  * @cfg {String} title Title of dialog
4267  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4268  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4269  * @cfg {Boolean} specificTitle default false
4270  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4271  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4272  * @cfg {Boolean} animate default true
4273  * @cfg {Boolean} allow_close default true
4274  * @cfg {Boolean} fitwindow default false
4275  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4276  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4277  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4278  * @cfg {String} size (sm|lg|xl) default empty
4279  * @cfg {Number} max_width set the max width of modal
4280  * @cfg {Boolean} editableTitle can the title be edited
4281
4282  *
4283  *
4284  * @constructor
4285  * Create a new Modal Dialog
4286  * @param {Object} config The config object
4287  */
4288
4289 Roo.bootstrap.Modal = function(config){
4290     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291     this.addEvents({
4292         // raw events
4293         /**
4294          * @event btnclick
4295          * The raw btnclick event for the button
4296          * @param {Roo.EventObject} e
4297          */
4298         "btnclick" : true,
4299         /**
4300          * @event resize
4301          * Fire when dialog resize
4302          * @param {Roo.bootstrap.Modal} this
4303          * @param {Roo.EventObject} e
4304          */
4305         "resize" : true,
4306         /**
4307          * @event titlechanged
4308          * Fire when the editable title has been changed
4309          * @param {Roo.bootstrap.Modal} this
4310          * @param {Roo.EventObject} value
4311          */
4312         "titlechanged" : true 
4313         
4314     });
4315     this.buttons = this.buttons || [];
4316
4317     if (this.tmpl) {
4318         this.tmpl = Roo.factory(this.tmpl);
4319     }
4320
4321 };
4322
4323 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4324
4325     title : 'test dialog',
4326
4327     buttons : false,
4328
4329     // set on load...
4330
4331     html: false,
4332
4333     tmp: false,
4334
4335     specificTitle: false,
4336
4337     buttonPosition: 'right',
4338
4339     allow_close : true,
4340
4341     animate : true,
4342
4343     fitwindow: false,
4344     
4345      // private
4346     dialogEl: false,
4347     bodyEl:  false,
4348     footerEl:  false,
4349     titleEl:  false,
4350     closeEl:  false,
4351
4352     size: '',
4353     
4354     max_width: 0,
4355     
4356     max_height: 0,
4357     
4358     fit_content: false,
4359     editableTitle  : false,
4360
4361     onRender : function(ct, position)
4362     {
4363         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364
4365         if(!this.el){
4366             var cfg = Roo.apply({},  this.getAutoCreate());
4367             cfg.id = Roo.id();
4368             //if(!cfg.name){
4369             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4370             //}
4371             //if (!cfg.name.length) {
4372             //    delete cfg.name;
4373            // }
4374             if (this.cls) {
4375                 cfg.cls += ' ' + this.cls;
4376             }
4377             if (this.style) {
4378                 cfg.style = this.style;
4379             }
4380             this.el = Roo.get(document.body).createChild(cfg, position);
4381         }
4382         //var type = this.el.dom.type;
4383
4384
4385         if(this.tabIndex !== undefined){
4386             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387         }
4388
4389         this.dialogEl = this.el.select('.modal-dialog',true).first();
4390         this.bodyEl = this.el.select('.modal-body',true).first();
4391         this.closeEl = this.el.select('.modal-header .close', true).first();
4392         this.headerEl = this.el.select('.modal-header',true).first();
4393         this.titleEl = this.el.select('.modal-title',true).first();
4394         this.footerEl = this.el.select('.modal-footer',true).first();
4395
4396         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4397         
4398         //this.el.addClass("x-dlg-modal");
4399
4400         if (this.buttons.length) {
4401             Roo.each(this.buttons, function(bb) {
4402                 var b = Roo.apply({}, bb);
4403                 b.xns = b.xns || Roo.bootstrap;
4404                 b.xtype = b.xtype || 'Button';
4405                 if (typeof(b.listeners) == 'undefined') {
4406                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4407                 }
4408
4409                 var btn = Roo.factory(b);
4410
4411                 btn.render(this.getButtonContainer());
4412
4413             },this);
4414         }
4415         // render the children.
4416         var nitems = [];
4417
4418         if(typeof(this.items) != 'undefined'){
4419             var items = this.items;
4420             delete this.items;
4421
4422             for(var i =0;i < items.length;i++) {
4423                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4424             }
4425         }
4426
4427         this.items = nitems;
4428
4429         // where are these used - they used to be body/close/footer
4430
4431
4432         this.initEvents();
4433         //this.el.addClass([this.fieldClass, this.cls]);
4434
4435     },
4436
4437     getAutoCreate : function()
4438     {
4439         // we will default to modal-body-overflow - might need to remove or make optional later.
4440         var bdy = {
4441                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4442                 html : this.html || ''
4443         };
4444
4445         var title = {
4446             tag: 'h5',
4447             cls : 'modal-title',
4448             html : this.title
4449         };
4450
4451         if(this.specificTitle){ // WTF is this?
4452             title = this.title;
4453         }
4454
4455         var header = [];
4456         if (this.allow_close && Roo.bootstrap.version == 3) {
4457             header.push({
4458                 tag: 'button',
4459                 cls : 'close',
4460                 html : '&times'
4461             });
4462         }
4463
4464         header.push(title);
4465
4466         if (this.editableTitle) {
4467             header.push({
4468                 cls: 'form-control roo-editable-title d-none',
4469                 tag: 'input',
4470                 type: 'text'
4471             });
4472         }
4473         
4474         if (this.allow_close && Roo.bootstrap.version == 4) {
4475             header.push({
4476                 tag: 'button',
4477                 cls : 'close',
4478                 html : '&times'
4479             });
4480         }
4481         
4482         var size = '';
4483
4484         if(this.size.length){
4485             size = 'modal-' + this.size;
4486         }
4487         
4488         var footer = Roo.bootstrap.version == 3 ?
4489             {
4490                 cls : 'modal-footer',
4491                 cn : [
4492                     {
4493                         tag: 'div',
4494                         cls: 'btn-' + this.buttonPosition
4495                     }
4496                 ]
4497
4498             } :
4499             {  // BS4 uses mr-auto on left buttons....
4500                 cls : 'modal-footer'
4501             };
4502
4503             
4504
4505         
4506         
4507         var modal = {
4508             cls: "modal",
4509              cn : [
4510                 {
4511                     cls: "modal-dialog " + size,
4512                     cn : [
4513                         {
4514                             cls : "modal-content",
4515                             cn : [
4516                                 {
4517                                     cls : 'modal-header',
4518                                     cn : header
4519                                 },
4520                                 bdy,
4521                                 footer
4522                             ]
4523
4524                         }
4525                     ]
4526
4527                 }
4528             ]
4529         };
4530
4531         if(this.animate){
4532             modal.cls += ' fade';
4533         }
4534
4535         return modal;
4536
4537     },
4538     getChildContainer : function() {
4539
4540          return this.bodyEl;
4541
4542     },
4543     getButtonContainer : function() {
4544         
4545          return Roo.bootstrap.version == 4 ?
4546             this.el.select('.modal-footer',true).first()
4547             : this.el.select('.modal-footer div',true).first();
4548
4549     },
4550     initEvents : function()
4551     {
4552         if (this.allow_close) {
4553             this.closeEl.on('click', this.hide, this);
4554         }
4555         Roo.EventManager.onWindowResize(this.resize, this, true);
4556         if (this.editableTitle) {
4557             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4558             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559             this.headerEditEl.on('keyup', function(e) {
4560                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561                         this.toggleHeaderInput(false)
4562                     }
4563                 }, this);
4564             this.headerEditEl.on('blur', function(e) {
4565                 this.toggleHeaderInput(false)
4566             },this);
4567         }
4568
4569     },
4570   
4571
4572     resize : function()
4573     {
4574         this.maskEl.setSize(
4575             Roo.lib.Dom.getViewWidth(true),
4576             Roo.lib.Dom.getViewHeight(true)
4577         );
4578         
4579         if (this.fitwindow) {
4580             
4581            this.dialogEl.setStyle( { 'max-width' : '100%' });
4582             this.setSize(
4583                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4585             );
4586             return;
4587         }
4588         
4589         if(this.max_width !== 0) {
4590             
4591             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592             
4593             if(this.height) {
4594                 this.setSize(w, this.height);
4595                 return;
4596             }
4597             
4598             if(this.max_height) {
4599                 this.setSize(w,Math.min(
4600                     this.max_height,
4601                     Roo.lib.Dom.getViewportHeight(true) - 60
4602                 ));
4603                 
4604                 return;
4605             }
4606             
4607             if(!this.fit_content) {
4608                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4609                 return;
4610             }
4611             
4612             this.setSize(w, Math.min(
4613                 60 +
4614                 this.headerEl.getHeight() + 
4615                 this.footerEl.getHeight() + 
4616                 this.getChildHeight(this.bodyEl.dom.childNodes),
4617                 Roo.lib.Dom.getViewportHeight(true) - 60)
4618             );
4619         }
4620         
4621     },
4622
4623     setSize : function(w,h)
4624     {
4625         if (!w && !h) {
4626             return;
4627         }
4628         
4629         this.resizeTo(w,h);
4630     },
4631
4632     show : function() {
4633
4634         if (!this.rendered) {
4635             this.render();
4636         }
4637         this.toggleHeaderInput(false);
4638         //this.el.setStyle('display', 'block');
4639         this.el.removeClass('hideing');
4640         this.el.dom.style.display='block';
4641         
4642         Roo.get(document.body).addClass('modal-open');
4643  
4644         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4645             
4646             (function(){
4647                 this.el.addClass('show');
4648                 this.el.addClass('in');
4649             }).defer(50, this);
4650         }else{
4651             this.el.addClass('show');
4652             this.el.addClass('in');
4653         }
4654
4655         // not sure how we can show data in here..
4656         //if (this.tmpl) {
4657         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4658         //}
4659
4660         Roo.get(document.body).addClass("x-body-masked");
4661         
4662         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4663         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664         this.maskEl.dom.style.display = 'block';
4665         this.maskEl.addClass('show');
4666         
4667         
4668         this.resize();
4669         
4670         this.fireEvent('show', this);
4671
4672         // set zindex here - otherwise it appears to be ignored...
4673         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4674
4675         (function () {
4676             this.items.forEach( function(e) {
4677                 e.layout ? e.layout() : false;
4678
4679             });
4680         }).defer(100,this);
4681
4682     },
4683     hide : function()
4684     {
4685         if(this.fireEvent("beforehide", this) !== false){
4686             
4687             this.maskEl.removeClass('show');
4688             
4689             this.maskEl.dom.style.display = '';
4690             Roo.get(document.body).removeClass("x-body-masked");
4691             this.el.removeClass('in');
4692             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4693
4694             if(this.animate){ // why
4695                 this.el.addClass('hideing');
4696                 this.el.removeClass('show');
4697                 (function(){
4698                     if (!this.el.hasClass('hideing')) {
4699                         return; // it's been shown again...
4700                     }
4701                     
4702                     this.el.dom.style.display='';
4703
4704                     Roo.get(document.body).removeClass('modal-open');
4705                     this.el.removeClass('hideing');
4706                 }).defer(150,this);
4707                 
4708             }else{
4709                 this.el.removeClass('show');
4710                 this.el.dom.style.display='';
4711                 Roo.get(document.body).removeClass('modal-open');
4712
4713             }
4714             this.fireEvent('hide', this);
4715         }
4716     },
4717     isVisible : function()
4718     {
4719         
4720         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4721         
4722     },
4723
4724     addButton : function(str, cb)
4725     {
4726
4727
4728         var b = Roo.apply({}, { html : str } );
4729         b.xns = b.xns || Roo.bootstrap;
4730         b.xtype = b.xtype || 'Button';
4731         if (typeof(b.listeners) == 'undefined') {
4732             b.listeners = { click : cb.createDelegate(this)  };
4733         }
4734
4735         var btn = Roo.factory(b);
4736
4737         btn.render(this.getButtonContainer());
4738
4739         return btn;
4740
4741     },
4742
4743     setDefaultButton : function(btn)
4744     {
4745         //this.el.select('.modal-footer').()
4746     },
4747
4748     resizeTo: function(w,h)
4749     {
4750         this.dialogEl.setWidth(w);
4751         
4752         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4753
4754         this.bodyEl.setHeight(h - diff);
4755         
4756         this.fireEvent('resize', this);
4757     },
4758     
4759     setContentSize  : function(w, h)
4760     {
4761
4762     },
4763     onButtonClick: function(btn,e)
4764     {
4765         //Roo.log([a,b,c]);
4766         this.fireEvent('btnclick', btn.name, e);
4767     },
4768      /**
4769      * Set the title of the Dialog
4770      * @param {String} str new Title
4771      */
4772     setTitle: function(str) {
4773         this.titleEl.dom.innerHTML = str;
4774         this.title = str;
4775     },
4776     /**
4777      * Set the body of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setBody: function(str) {
4781         this.bodyEl.dom.innerHTML = str;
4782     },
4783     /**
4784      * Set the body of the Dialog using the template
4785      * @param {Obj} data - apply this data to the template and replace the body contents.
4786      */
4787     applyBody: function(obj)
4788     {
4789         if (!this.tmpl) {
4790             Roo.log("Error - using apply Body without a template");
4791             //code
4792         }
4793         this.tmpl.overwrite(this.bodyEl, obj);
4794     },
4795     
4796     getChildHeight : function(child_nodes)
4797     {
4798         if(
4799             !child_nodes ||
4800             child_nodes.length == 0
4801         ) {
4802             return 0;
4803         }
4804         
4805         var child_height = 0;
4806         
4807         for(var i = 0; i < child_nodes.length; i++) {
4808             
4809             /*
4810             * for modal with tabs...
4811             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4812                 
4813                 var layout_childs = child_nodes[i].childNodes;
4814                 
4815                 for(var j = 0; j < layout_childs.length; j++) {
4816                     
4817                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4818                         
4819                         var layout_body_childs = layout_childs[j].childNodes;
4820                         
4821                         for(var k = 0; k < layout_body_childs.length; k++) {
4822                             
4823                             if(layout_body_childs[k].classList.contains('navbar')) {
4824                                 child_height += layout_body_childs[k].offsetHeight;
4825                                 continue;
4826                             }
4827                             
4828                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4829                                 
4830                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4831                                 
4832                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4833                                     
4834                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4835                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4836                                         continue;
4837                                     }
4838                                     
4839                                 }
4840                                 
4841                             }
4842                             
4843                         }
4844                     }
4845                 }
4846                 continue;
4847             }
4848             */
4849             
4850             child_height += child_nodes[i].offsetHeight;
4851             // Roo.log(child_nodes[i].offsetHeight);
4852         }
4853         
4854         return child_height;
4855     },
4856     toggleHeaderInput : function(is_edit)
4857     {
4858         if (!this.editableTitle) {
4859             return; // not editable.
4860         }
4861         if (is_edit && this.is_header_editing) {
4862             return; // already editing..
4863         }
4864         if (is_edit) {
4865     
4866             this.headerEditEl.dom.value = this.title;
4867             this.headerEditEl.removeClass('d-none');
4868             this.headerEditEl.dom.focus();
4869             this.titleEl.addClass('d-none');
4870             
4871             this.is_header_editing = true;
4872             return
4873         }
4874         // flip back to not editing.
4875         this.title = this.headerEditEl.dom.value;
4876         this.headerEditEl.addClass('d-none');
4877         this.titleEl.removeClass('d-none');
4878         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4879         this.is_header_editing = false;
4880         this.fireEvent('titlechanged', this, this.title);
4881     
4882             
4883         
4884     }
4885
4886 });
4887
4888
4889 Roo.apply(Roo.bootstrap.Modal,  {
4890     /**
4891          * Button config that displays a single OK button
4892          * @type Object
4893          */
4894         OK :  [{
4895             name : 'ok',
4896             weight : 'primary',
4897             html : 'OK'
4898         }],
4899         /**
4900          * Button config that displays Yes and No buttons
4901          * @type Object
4902          */
4903         YESNO : [
4904             {
4905                 name  : 'no',
4906                 html : 'No'
4907             },
4908             {
4909                 name  :'yes',
4910                 weight : 'primary',
4911                 html : 'Yes'
4912             }
4913         ],
4914
4915         /**
4916          * Button config that displays OK and Cancel buttons
4917          * @type Object
4918          */
4919         OKCANCEL : [
4920             {
4921                name : 'cancel',
4922                 html : 'Cancel'
4923             },
4924             {
4925                 name : 'ok',
4926                 weight : 'primary',
4927                 html : 'OK'
4928             }
4929         ],
4930         /**
4931          * Button config that displays Yes, No and Cancel buttons
4932          * @type Object
4933          */
4934         YESNOCANCEL : [
4935             {
4936                 name : 'yes',
4937                 weight : 'primary',
4938                 html : 'Yes'
4939             },
4940             {
4941                 name : 'no',
4942                 html : 'No'
4943             },
4944             {
4945                 name : 'cancel',
4946                 html : 'Cancel'
4947             }
4948         ],
4949         
4950         zIndex : 10001
4951 });
4952
4953 /*
4954  * - LGPL
4955  *
4956  * messagebox - can be used as a replace
4957  * 
4958  */
4959 /**
4960  * @class Roo.MessageBox
4961  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4962  * Example usage:
4963  *<pre><code>
4964 // Basic alert:
4965 Roo.Msg.alert('Status', 'Changes saved successfully.');
4966
4967 // Prompt for user data:
4968 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4969     if (btn == 'ok'){
4970         // process text value...
4971     }
4972 });
4973
4974 // Show a dialog using config options:
4975 Roo.Msg.show({
4976    title:'Save Changes?',
4977    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4978    buttons: Roo.Msg.YESNOCANCEL,
4979    fn: processResult,
4980    animEl: 'elId'
4981 });
4982 </code></pre>
4983  * @static
4984  */
4985 Roo.bootstrap.MessageBox = function(){
4986     var dlg, opt, mask, waitTimer;
4987     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4988     var buttons, activeTextEl, bwidth;
4989
4990     
4991     // private
4992     var handleButton = function(button){
4993         dlg.hide();
4994         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4995     };
4996
4997     // private
4998     var handleHide = function(){
4999         if(opt && opt.cls){
5000             dlg.el.removeClass(opt.cls);
5001         }
5002         //if(waitTimer){
5003         //    Roo.TaskMgr.stop(waitTimer);
5004         //    waitTimer = null;
5005         //}
5006     };
5007
5008     // private
5009     var updateButtons = function(b){
5010         var width = 0;
5011         if(!b){
5012             buttons["ok"].hide();
5013             buttons["cancel"].hide();
5014             buttons["yes"].hide();
5015             buttons["no"].hide();
5016             dlg.footerEl.hide();
5017             
5018             return width;
5019         }
5020         dlg.footerEl.show();
5021         for(var k in buttons){
5022             if(typeof buttons[k] != "function"){
5023                 if(b[k]){
5024                     buttons[k].show();
5025                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5026                     width += buttons[k].el.getWidth()+15;
5027                 }else{
5028                     buttons[k].hide();
5029                 }
5030             }
5031         }
5032         return width;
5033     };
5034
5035     // private
5036     var handleEsc = function(d, k, e){
5037         if(opt && opt.closable !== false){
5038             dlg.hide();
5039         }
5040         if(e){
5041             e.stopEvent();
5042         }
5043     };
5044
5045     return {
5046         /**
5047          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5048          * @return {Roo.BasicDialog} The BasicDialog element
5049          */
5050         getDialog : function(){
5051            if(!dlg){
5052                 dlg = new Roo.bootstrap.Modal( {
5053                     //draggable: true,
5054                     //resizable:false,
5055                     //constraintoviewport:false,
5056                     //fixedcenter:true,
5057                     //collapsible : false,
5058                     //shim:true,
5059                     //modal: true,
5060                 //    width: 'auto',
5061                   //  height:100,
5062                     //buttonAlign:"center",
5063                     closeClick : function(){
5064                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5065                             handleButton("no");
5066                         }else{
5067                             handleButton("cancel");
5068                         }
5069                     }
5070                 });
5071                 dlg.render();
5072                 dlg.on("hide", handleHide);
5073                 mask = dlg.mask;
5074                 //dlg.addKeyListener(27, handleEsc);
5075                 buttons = {};
5076                 this.buttons = buttons;
5077                 var bt = this.buttonText;
5078                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5079                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5080                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5081                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5082                 //Roo.log(buttons);
5083                 bodyEl = dlg.bodyEl.createChild({
5084
5085                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5086                         '<textarea class="roo-mb-textarea"></textarea>' +
5087                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5088                 });
5089                 msgEl = bodyEl.dom.firstChild;
5090                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5091                 textboxEl.enableDisplayMode();
5092                 textboxEl.addKeyListener([10,13], function(){
5093                     if(dlg.isVisible() && opt && opt.buttons){
5094                         if(opt.buttons.ok){
5095                             handleButton("ok");
5096                         }else if(opt.buttons.yes){
5097                             handleButton("yes");
5098                         }
5099                     }
5100                 });
5101                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5102                 textareaEl.enableDisplayMode();
5103                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5104                 progressEl.enableDisplayMode();
5105                 
5106                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5107                 var pf = progressEl.dom.firstChild;
5108                 if (pf) {
5109                     pp = Roo.get(pf.firstChild);
5110                     pp.setHeight(pf.offsetHeight);
5111                 }
5112                 
5113             }
5114             return dlg;
5115         },
5116
5117         /**
5118          * Updates the message box body text
5119          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5120          * the XHTML-compliant non-breaking space character '&amp;#160;')
5121          * @return {Roo.MessageBox} This message box
5122          */
5123         updateText : function(text)
5124         {
5125             if(!dlg.isVisible() && !opt.width){
5126                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5127                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5128             }
5129             msgEl.innerHTML = text || '&#160;';
5130       
5131             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5132             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5133             var w = Math.max(
5134                     Math.min(opt.width || cw , this.maxWidth), 
5135                     Math.max(opt.minWidth || this.minWidth, bwidth)
5136             );
5137             if(opt.prompt){
5138                 activeTextEl.setWidth(w);
5139             }
5140             if(dlg.isVisible()){
5141                 dlg.fixedcenter = false;
5142             }
5143             // to big, make it scroll. = But as usual stupid IE does not support
5144             // !important..
5145             
5146             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5147                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5148                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5149             } else {
5150                 bodyEl.dom.style.height = '';
5151                 bodyEl.dom.style.overflowY = '';
5152             }
5153             if (cw > w) {
5154                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5155             } else {
5156                 bodyEl.dom.style.overflowX = '';
5157             }
5158             
5159             dlg.setContentSize(w, bodyEl.getHeight());
5160             if(dlg.isVisible()){
5161                 dlg.fixedcenter = true;
5162             }
5163             return this;
5164         },
5165
5166         /**
5167          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5168          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5169          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5170          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5171          * @return {Roo.MessageBox} This message box
5172          */
5173         updateProgress : function(value, text){
5174             if(text){
5175                 this.updateText(text);
5176             }
5177             
5178             if (pp) { // weird bug on my firefox - for some reason this is not defined
5179                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5180                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5181             }
5182             return this;
5183         },        
5184
5185         /**
5186          * Returns true if the message box is currently displayed
5187          * @return {Boolean} True if the message box is visible, else false
5188          */
5189         isVisible : function(){
5190             return dlg && dlg.isVisible();  
5191         },
5192
5193         /**
5194          * Hides the message box if it is displayed
5195          */
5196         hide : function(){
5197             if(this.isVisible()){
5198                 dlg.hide();
5199             }  
5200         },
5201
5202         /**
5203          * Displays a new message box, or reinitializes an existing message box, based on the config options
5204          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5205          * The following config object properties are supported:
5206          * <pre>
5207 Property    Type             Description
5208 ----------  ---------------  ------------------------------------------------------------------------------------
5209 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5210                                    closes (defaults to undefined)
5211 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5212                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5213 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5214                                    progress and wait dialogs will ignore this property and always hide the
5215                                    close button as they can only be closed programmatically.
5216 cls               String           A custom CSS class to apply to the message box element
5217 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5218                                    displayed (defaults to 75)
5219 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5220                                    function will be btn (the name of the button that was clicked, if applicable,
5221                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5222                                    Progress and wait dialogs will ignore this option since they do not respond to
5223                                    user actions and can only be closed programmatically, so any required function
5224                                    should be called by the same code after it closes the dialog.
5225 icon              String           A CSS class that provides a background image to be used as an icon for
5226                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5227 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5228 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5229 modal             Boolean          False to allow user interaction with the page while the message box is
5230                                    displayed (defaults to true)
5231 msg               String           A string that will replace the existing message box body text (defaults
5232                                    to the XHTML-compliant non-breaking space character '&#160;')
5233 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5234 progress          Boolean          True to display a progress bar (defaults to false)
5235 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5236 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5237 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5238 title             String           The title text
5239 value             String           The string value to set into the active textbox element if displayed
5240 wait              Boolean          True to display a progress bar (defaults to false)
5241 width             Number           The width of the dialog in pixels
5242 </pre>
5243          *
5244          * Example usage:
5245          * <pre><code>
5246 Roo.Msg.show({
5247    title: 'Address',
5248    msg: 'Please enter your address:',
5249    width: 300,
5250    buttons: Roo.MessageBox.OKCANCEL,
5251    multiline: true,
5252    fn: saveAddress,
5253    animEl: 'addAddressBtn'
5254 });
5255 </code></pre>
5256          * @param {Object} config Configuration options
5257          * @return {Roo.MessageBox} This message box
5258          */
5259         show : function(options)
5260         {
5261             
5262             // this causes nightmares if you show one dialog after another
5263             // especially on callbacks..
5264              
5265             if(this.isVisible()){
5266                 
5267                 this.hide();
5268                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5269                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5270                 Roo.log("New Dialog Message:" +  options.msg )
5271                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5272                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5273                 
5274             }
5275             var d = this.getDialog();
5276             opt = options;
5277             d.setTitle(opt.title || "&#160;");
5278             d.closeEl.setDisplayed(opt.closable !== false);
5279             activeTextEl = textboxEl;
5280             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5281             if(opt.prompt){
5282                 if(opt.multiline){
5283                     textboxEl.hide();
5284                     textareaEl.show();
5285                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5286                         opt.multiline : this.defaultTextHeight);
5287                     activeTextEl = textareaEl;
5288                 }else{
5289                     textboxEl.show();
5290                     textareaEl.hide();
5291                 }
5292             }else{
5293                 textboxEl.hide();
5294                 textareaEl.hide();
5295             }
5296             progressEl.setDisplayed(opt.progress === true);
5297             if (opt.progress) {
5298                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5299             }
5300             this.updateProgress(0);
5301             activeTextEl.dom.value = opt.value || "";
5302             if(opt.prompt){
5303                 dlg.setDefaultButton(activeTextEl);
5304             }else{
5305                 var bs = opt.buttons;
5306                 var db = null;
5307                 if(bs && bs.ok){
5308                     db = buttons["ok"];
5309                 }else if(bs && bs.yes){
5310                     db = buttons["yes"];
5311                 }
5312                 dlg.setDefaultButton(db);
5313             }
5314             bwidth = updateButtons(opt.buttons);
5315             this.updateText(opt.msg);
5316             if(opt.cls){
5317                 d.el.addClass(opt.cls);
5318             }
5319             d.proxyDrag = opt.proxyDrag === true;
5320             d.modal = opt.modal !== false;
5321             d.mask = opt.modal !== false ? mask : false;
5322             if(!d.isVisible()){
5323                 // force it to the end of the z-index stack so it gets a cursor in FF
5324                 document.body.appendChild(dlg.el.dom);
5325                 d.animateTarget = null;
5326                 d.show(options.animEl);
5327             }
5328             return this;
5329         },
5330
5331         /**
5332          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5333          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5334          * and closing the message box when the process is complete.
5335          * @param {String} title The title bar text
5336          * @param {String} msg The message box body text
5337          * @return {Roo.MessageBox} This message box
5338          */
5339         progress : function(title, msg){
5340             this.show({
5341                 title : title,
5342                 msg : msg,
5343                 buttons: false,
5344                 progress:true,
5345                 closable:false,
5346                 minWidth: this.minProgressWidth,
5347                 modal : true
5348             });
5349             return this;
5350         },
5351
5352         /**
5353          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5354          * If a callback function is passed it will be called after the user clicks the button, and the
5355          * id of the button that was clicked will be passed as the only parameter to the callback
5356          * (could also be the top-right close button).
5357          * @param {String} title The title bar text
5358          * @param {String} msg The message box body text
5359          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5360          * @param {Object} scope (optional) The scope of the callback function
5361          * @return {Roo.MessageBox} This message box
5362          */
5363         alert : function(title, msg, fn, scope)
5364         {
5365             this.show({
5366                 title : title,
5367                 msg : msg,
5368                 buttons: this.OK,
5369                 fn: fn,
5370                 closable : false,
5371                 scope : scope,
5372                 modal : true
5373             });
5374             return this;
5375         },
5376
5377         /**
5378          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5379          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5380          * You are responsible for closing the message box when the process is complete.
5381          * @param {String} msg The message box body text
5382          * @param {String} title (optional) The title bar text
5383          * @return {Roo.MessageBox} This message box
5384          */
5385         wait : function(msg, title){
5386             this.show({
5387                 title : title,
5388                 msg : msg,
5389                 buttons: false,
5390                 closable:false,
5391                 progress:true,
5392                 modal:true,
5393                 width:300,
5394                 wait:true
5395             });
5396             waitTimer = Roo.TaskMgr.start({
5397                 run: function(i){
5398                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5399                 },
5400                 interval: 1000
5401             });
5402             return this;
5403         },
5404
5405         /**
5406          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5407          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5408          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5409          * @param {String} title The title bar text
5410          * @param {String} msg The message box body text
5411          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5412          * @param {Object} scope (optional) The scope of the callback function
5413          * @return {Roo.MessageBox} This message box
5414          */
5415         confirm : function(title, msg, fn, scope){
5416             this.show({
5417                 title : title,
5418                 msg : msg,
5419                 buttons: this.YESNO,
5420                 fn: fn,
5421                 scope : scope,
5422                 modal : true
5423             });
5424             return this;
5425         },
5426
5427         /**
5428          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5429          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5430          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5431          * (could also be the top-right close button) and the text that was entered will be passed as the two
5432          * parameters to the callback.
5433          * @param {String} title The title bar text
5434          * @param {String} msg The message box body text
5435          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5436          * @param {Object} scope (optional) The scope of the callback function
5437          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5438          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5439          * @return {Roo.MessageBox} This message box
5440          */
5441         prompt : function(title, msg, fn, scope, multiline){
5442             this.show({
5443                 title : title,
5444                 msg : msg,
5445                 buttons: this.OKCANCEL,
5446                 fn: fn,
5447                 minWidth:250,
5448                 scope : scope,
5449                 prompt:true,
5450                 multiline: multiline,
5451                 modal : true
5452             });
5453             return this;
5454         },
5455
5456         /**
5457          * Button config that displays a single OK button
5458          * @type Object
5459          */
5460         OK : {ok:true},
5461         /**
5462          * Button config that displays Yes and No buttons
5463          * @type Object
5464          */
5465         YESNO : {yes:true, no:true},
5466         /**
5467          * Button config that displays OK and Cancel buttons
5468          * @type Object
5469          */
5470         OKCANCEL : {ok:true, cancel:true},
5471         /**
5472          * Button config that displays Yes, No and Cancel buttons
5473          * @type Object
5474          */
5475         YESNOCANCEL : {yes:true, no:true, cancel:true},
5476
5477         /**
5478          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5479          * @type Number
5480          */
5481         defaultTextHeight : 75,
5482         /**
5483          * The maximum width in pixels of the message box (defaults to 600)
5484          * @type Number
5485          */
5486         maxWidth : 600,
5487         /**
5488          * The minimum width in pixels of the message box (defaults to 100)
5489          * @type Number
5490          */
5491         minWidth : 100,
5492         /**
5493          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5494          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5495          * @type Number
5496          */
5497         minProgressWidth : 250,
5498         /**
5499          * An object containing the default button text strings that can be overriden for localized language support.
5500          * Supported properties are: ok, cancel, yes and no.
5501          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5502          * @type Object
5503          */
5504         buttonText : {
5505             ok : "OK",
5506             cancel : "Cancel",
5507             yes : "Yes",
5508             no : "No"
5509         }
5510     };
5511 }();
5512
5513 /**
5514  * Shorthand for {@link Roo.MessageBox}
5515  */
5516 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5517 Roo.Msg = Roo.Msg || Roo.MessageBox;
5518 /*
5519  * - LGPL
5520  *
5521  * navbar
5522  * 
5523  */
5524
5525 /**
5526  * @class Roo.bootstrap.nav.Bar
5527  * @extends Roo.bootstrap.Component
5528  * @abstract
5529  * Bootstrap Navbar class
5530
5531  * @constructor
5532  * Create a new Navbar
5533  * @param {Object} config The config object
5534  */
5535
5536
5537 Roo.bootstrap.nav.Bar = function(config){
5538     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5539     this.addEvents({
5540         // raw events
5541         /**
5542          * @event beforetoggle
5543          * Fire before toggle the menu
5544          * @param {Roo.EventObject} e
5545          */
5546         "beforetoggle" : true
5547     });
5548 };
5549
5550 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5551     
5552     
5553    
5554     // private
5555     navItems : false,
5556     loadMask : false,
5557     
5558     
5559     getAutoCreate : function(){
5560         
5561         
5562         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5563         
5564     },
5565     
5566     initEvents :function ()
5567     {
5568         //Roo.log(this.el.select('.navbar-toggle',true));
5569         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5570         
5571         var mark = {
5572             tag: "div",
5573             cls:"x-dlg-mask"
5574         };
5575         
5576         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5577         
5578         var size = this.el.getSize();
5579         this.maskEl.setSize(size.width, size.height);
5580         this.maskEl.enableDisplayMode("block");
5581         this.maskEl.hide();
5582         
5583         if(this.loadMask){
5584             this.maskEl.show();
5585         }
5586     },
5587     
5588     
5589     getChildContainer : function()
5590     {
5591         if (this.el && this.el.select('.collapse').getCount()) {
5592             return this.el.select('.collapse',true).first();
5593         }
5594         
5595         return this.el;
5596     },
5597     
5598     mask : function()
5599     {
5600         this.maskEl.show();
5601     },
5602     
5603     unmask : function()
5604     {
5605         this.maskEl.hide();
5606     },
5607     onToggle : function()
5608     {
5609         
5610         if(this.fireEvent('beforetoggle', this) === false){
5611             return;
5612         }
5613         var ce = this.el.select('.navbar-collapse',true).first();
5614       
5615         if (!ce.hasClass('show')) {
5616            this.expand();
5617         } else {
5618             this.collapse();
5619         }
5620         
5621         
5622     
5623     },
5624     /**
5625      * Expand the navbar pulldown 
5626      */
5627     expand : function ()
5628     {
5629        
5630         var ce = this.el.select('.navbar-collapse',true).first();
5631         if (ce.hasClass('collapsing')) {
5632             return;
5633         }
5634         ce.dom.style.height = '';
5635                // show it...
5636         ce.addClass('in'); // old...
5637         ce.removeClass('collapse');
5638         ce.addClass('show');
5639         var h = ce.getHeight();
5640         Roo.log(h);
5641         ce.removeClass('show');
5642         // at this point we should be able to see it..
5643         ce.addClass('collapsing');
5644         
5645         ce.setHeight(0); // resize it ...
5646         ce.on('transitionend', function() {
5647             //Roo.log('done transition');
5648             ce.removeClass('collapsing');
5649             ce.addClass('show');
5650             ce.removeClass('collapse');
5651
5652             ce.dom.style.height = '';
5653         }, this, { single: true} );
5654         ce.setHeight(h);
5655         ce.dom.scrollTop = 0;
5656     },
5657     /**
5658      * Collapse the navbar pulldown 
5659      */
5660     collapse : function()
5661     {
5662          var ce = this.el.select('.navbar-collapse',true).first();
5663        
5664         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5665             // it's collapsed or collapsing..
5666             return;
5667         }
5668         ce.removeClass('in'); // old...
5669         ce.setHeight(ce.getHeight());
5670         ce.removeClass('show');
5671         ce.addClass('collapsing');
5672         
5673         ce.on('transitionend', function() {
5674             ce.dom.style.height = '';
5675             ce.removeClass('collapsing');
5676             ce.addClass('collapse');
5677         }, this, { single: true} );
5678         ce.setHeight(0);
5679     }
5680     
5681     
5682     
5683 });
5684
5685
5686
5687  
5688
5689  /*
5690  * - LGPL
5691  *
5692  * navbar
5693  * 
5694  */
5695
5696 /**
5697  * @class Roo.bootstrap.nav.Simplebar
5698  * @extends Roo.bootstrap.nav.Bar
5699  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5700  * Bootstrap Sidebar class
5701  *
5702  * @cfg {Boolean} inverse is inverted color
5703  * 
5704  * @cfg {String} type (nav | pills | tabs)
5705  * @cfg {Boolean} arrangement stacked | justified
5706  * @cfg {String} align (left | right) alignment
5707  * 
5708  * @cfg {Boolean} main (true|false) main nav bar? default false
5709  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5710  * 
5711  * @cfg {String} tag (header|footer|nav|div) default is nav 
5712
5713  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5714  * 
5715  * 
5716  * @constructor
5717  * Create a new Sidebar
5718  * @param {Object} config The config object
5719  */
5720
5721
5722 Roo.bootstrap.nav.Simplebar = function(config){
5723     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5724 };
5725
5726 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5727     
5728     inverse: false,
5729     
5730     type: false,
5731     arrangement: '',
5732     align : false,
5733     
5734     weight : 'light',
5735     
5736     main : false,
5737     
5738     
5739     tag : false,
5740     
5741     
5742     getAutoCreate : function(){
5743         
5744         
5745         var cfg = {
5746             tag : this.tag || 'div',
5747             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5748         };
5749         if (['light','white'].indexOf(this.weight) > -1) {
5750             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5751         }
5752         cfg.cls += ' bg-' + this.weight;
5753         
5754         if (this.inverse) {
5755             cfg.cls += ' navbar-inverse';
5756             
5757         }
5758         
5759         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5760         
5761         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5762             return cfg;
5763         }
5764         
5765         
5766     
5767         
5768         cfg.cn = [
5769             {
5770                 cls: 'nav nav-' + this.xtype,
5771                 tag : 'ul'
5772             }
5773         ];
5774         
5775          
5776         this.type = this.type || 'nav';
5777         if (['tabs','pills'].indexOf(this.type) != -1) {
5778             cfg.cn[0].cls += ' nav-' + this.type
5779         
5780         
5781         } else {
5782             if (this.type!=='nav') {
5783                 Roo.log('nav type must be nav/tabs/pills')
5784             }
5785             cfg.cn[0].cls += ' navbar-nav'
5786         }
5787         
5788         
5789         
5790         
5791         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5792             cfg.cn[0].cls += ' nav-' + this.arrangement;
5793         }
5794         
5795         
5796         if (this.align === 'right') {
5797             cfg.cn[0].cls += ' navbar-right';
5798         }
5799         
5800         
5801         
5802         
5803         return cfg;
5804     
5805         
5806     }
5807     
5808     
5809     
5810 });
5811
5812
5813
5814  
5815
5816  
5817        /*
5818  * - LGPL
5819  *
5820  * navbar
5821  * navbar-fixed-top
5822  * navbar-expand-md  fixed-top 
5823  */
5824
5825 /**
5826  * @class Roo.bootstrap.nav.Headerbar
5827  * @extends Roo.bootstrap.nav.Simplebar
5828  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5829  * Bootstrap Sidebar class
5830  *
5831  * @cfg {String} brand what is brand
5832  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5833  * @cfg {String} brand_href href of the brand
5834  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5835  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5836  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5837  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5838  * 
5839  * @constructor
5840  * Create a new Sidebar
5841  * @param {Object} config The config object
5842  */
5843
5844
5845 Roo.bootstrap.nav.Headerbar = function(config){
5846     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5847       
5848 };
5849
5850 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5851     
5852     position: '',
5853     brand: '',
5854     brand_href: false,
5855     srButton : true,
5856     autohide : false,
5857     desktopCenter : false,
5858    
5859     
5860     getAutoCreate : function(){
5861         
5862         var   cfg = {
5863             tag: this.nav || 'nav',
5864             cls: 'navbar navbar-expand-md',
5865             role: 'navigation',
5866             cn: []
5867         };
5868         
5869         var cn = cfg.cn;
5870         if (this.desktopCenter) {
5871             cn.push({cls : 'container', cn : []});
5872             cn = cn[0].cn;
5873         }
5874         
5875         if(this.srButton){
5876             var btn = {
5877                 tag: 'button',
5878                 type: 'button',
5879                 cls: 'navbar-toggle navbar-toggler',
5880                 'data-toggle': 'collapse',
5881                 cn: [
5882                     {
5883                         tag: 'span',
5884                         cls: 'sr-only',
5885                         html: 'Toggle navigation'
5886                     },
5887                     {
5888                         tag: 'span',
5889                         cls: 'icon-bar navbar-toggler-icon'
5890                     },
5891                     {
5892                         tag: 'span',
5893                         cls: 'icon-bar'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar'
5898                     }
5899                 ]
5900             };
5901             
5902             cn.push( Roo.bootstrap.version == 4 ? btn : {
5903                 tag: 'div',
5904                 cls: 'navbar-header',
5905                 cn: [
5906                     btn
5907                 ]
5908             });
5909         }
5910         
5911         cn.push({
5912             tag: 'div',
5913             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5914             cn : []
5915         });
5916         
5917         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5918         
5919         if (['light','white'].indexOf(this.weight) > -1) {
5920             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5921         }
5922         cfg.cls += ' bg-' + this.weight;
5923         
5924         
5925         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5926             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5927             
5928             // tag can override this..
5929             
5930             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5931         }
5932         
5933         if (this.brand !== '') {
5934             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5935             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5936                 tag: 'a',
5937                 href: this.brand_href ? this.brand_href : '#',
5938                 cls: 'navbar-brand',
5939                 cn: [
5940                 this.brand
5941                 ]
5942             });
5943         }
5944         
5945         if(this.main){
5946             cfg.cls += ' main-nav';
5947         }
5948         
5949         
5950         return cfg;
5951
5952         
5953     },
5954     getHeaderChildContainer : function()
5955     {
5956         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5957             return this.el.select('.navbar-header',true).first();
5958         }
5959         
5960         return this.getChildContainer();
5961     },
5962     
5963     getChildContainer : function()
5964     {
5965          
5966         return this.el.select('.roo-navbar-collapse',true).first();
5967          
5968         
5969     },
5970     
5971     initEvents : function()
5972     {
5973         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5974         
5975         if (this.autohide) {
5976             
5977             var prevScroll = 0;
5978             var ft = this.el;
5979             
5980             Roo.get(document).on('scroll',function(e) {
5981                 var ns = Roo.get(document).getScroll().top;
5982                 var os = prevScroll;
5983                 prevScroll = ns;
5984                 
5985                 if(ns > os){
5986                     ft.removeClass('slideDown');
5987                     ft.addClass('slideUp');
5988                     return;
5989                 }
5990                 ft.removeClass('slideUp');
5991                 ft.addClass('slideDown');
5992                  
5993               
5994           },this);
5995         }
5996     }    
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * navbar
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.nav.Sidebar
6013  * @extends Roo.bootstrap.nav.Bar
6014  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6015  * Bootstrap Sidebar class
6016  * 
6017  * @constructor
6018  * Create a new Sidebar
6019  * @param {Object} config The config object
6020  */
6021
6022
6023 Roo.bootstrap.nav.Sidebar = function(config){
6024     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6025 };
6026
6027 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6028     
6029     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6030     
6031     getAutoCreate : function(){
6032         
6033         
6034         return  {
6035             tag: 'div',
6036             cls: 'sidebar sidebar-nav'
6037         };
6038     
6039         
6040     }
6041     
6042     
6043     
6044 });
6045
6046
6047
6048  
6049
6050  /*
6051  * - LGPL
6052  *
6053  * nav group
6054  * 
6055  */
6056
6057 /**
6058  * @class Roo.bootstrap.nav.Group
6059  * @extends Roo.bootstrap.Component
6060  * @children Roo.bootstrap.nav.Item
6061  * Bootstrap NavGroup class
6062  * @cfg {String} align (left|right)
6063  * @cfg {Boolean} inverse
6064  * @cfg {String} type (nav|pills|tab) default nav
6065  * @cfg {String} navId - reference Id for navbar.
6066  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6067  * 
6068  * @constructor
6069  * Create a new nav group
6070  * @param {Object} config The config object
6071  */
6072
6073 Roo.bootstrap.nav.Group = function(config){
6074     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6075     this.navItems = [];
6076    
6077     Roo.bootstrap.nav.Group.register(this);
6078      this.addEvents({
6079         /**
6080              * @event changed
6081              * Fires when the active item changes
6082              * @param {Roo.bootstrap.nav.Group} this
6083              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6084              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6085          */
6086         'changed': true
6087      });
6088     
6089 };
6090
6091 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6092     
6093     align: '',
6094     inverse: false,
6095     form: false,
6096     type: 'nav',
6097     navId : '',
6098     // private
6099     pilltype : true,
6100     
6101     navItems : false, 
6102     
6103     getAutoCreate : function()
6104     {
6105         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6106         
6107         cfg = {
6108             tag : 'ul',
6109             cls: 'nav' 
6110         };
6111         if (Roo.bootstrap.version == 4) {
6112             if (['tabs','pills'].indexOf(this.type) != -1) {
6113                 cfg.cls += ' nav-' + this.type; 
6114             } else {
6115                 // trying to remove so header bar can right align top?
6116                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6117                     // do not use on header bar... 
6118                     cfg.cls += ' navbar-nav';
6119                 }
6120             }
6121             
6122         } else {
6123             if (['tabs','pills'].indexOf(this.type) != -1) {
6124                 cfg.cls += ' nav-' + this.type
6125             } else {
6126                 if (this.type !== 'nav') {
6127                     Roo.log('nav type must be nav/tabs/pills')
6128                 }
6129                 cfg.cls += ' navbar-nav'
6130             }
6131         }
6132         
6133         if (this.parent() && this.parent().sidebar) {
6134             cfg = {
6135                 tag: 'ul',
6136                 cls: 'dashboard-menu sidebar-menu'
6137             };
6138             
6139             return cfg;
6140         }
6141         
6142         if (this.form === true) {
6143             cfg = {
6144                 tag: 'form',
6145                 cls: 'navbar-form form-inline'
6146             };
6147             //nav navbar-right ml-md-auto
6148             if (this.align === 'right') {
6149                 cfg.cls += ' navbar-right ml-md-auto';
6150             } else {
6151                 cfg.cls += ' navbar-left';
6152             }
6153         }
6154         
6155         if (this.align === 'right') {
6156             cfg.cls += ' navbar-right ml-md-auto';
6157         } else {
6158             cfg.cls += ' mr-auto';
6159         }
6160         
6161         if (this.inverse) {
6162             cfg.cls += ' navbar-inverse';
6163             
6164         }
6165         
6166         
6167         return cfg;
6168     },
6169     /**
6170     * sets the active Navigation item
6171     * @param {Roo.bootstrap.nav.Item} the new current navitem
6172     */
6173     setActiveItem : function(item)
6174     {
6175         var prev = false;
6176         Roo.each(this.navItems, function(v){
6177             if (v == item) {
6178                 return ;
6179             }
6180             if (v.isActive()) {
6181                 v.setActive(false, true);
6182                 prev = v;
6183                 
6184             }
6185             
6186         });
6187
6188         item.setActive(true, true);
6189         this.fireEvent('changed', this, item, prev);
6190         
6191         
6192     },
6193     /**
6194     * gets the active Navigation item
6195     * @return {Roo.bootstrap.nav.Item} the current navitem
6196     */
6197     getActive : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v){
6202             
6203             if (v.isActive()) {
6204                 prev = v;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     
6212     indexOfNav : function()
6213     {
6214         
6215         var prev = false;
6216         Roo.each(this.navItems, function(v,i){
6217             
6218             if (v.isActive()) {
6219                 prev = i;
6220                 
6221             }
6222             
6223         });
6224         return prev;
6225     },
6226     /**
6227     * adds a Navigation item
6228     * @param {Roo.bootstrap.nav.Item} the navitem to add
6229     */
6230     addItem : function(cfg)
6231     {
6232         if (this.form && Roo.bootstrap.version == 4) {
6233             cfg.tag = 'div';
6234         }
6235         var cn = new Roo.bootstrap.nav.Item(cfg);
6236         this.register(cn);
6237         cn.parentId = this.id;
6238         cn.onRender(this.el, null);
6239         return cn;
6240     },
6241     /**
6242     * register a Navigation item
6243     * @param {Roo.bootstrap.nav.Item} the navitem to add
6244     */
6245     register : function(item)
6246     {
6247         this.navItems.push( item);
6248         item.navId = this.navId;
6249     
6250     },
6251     
6252     /**
6253     * clear all the Navigation item
6254     */
6255    
6256     clearAll : function()
6257     {
6258         this.navItems = [];
6259         this.el.dom.innerHTML = '';
6260     },
6261     
6262     getNavItem: function(tabId)
6263     {
6264         var ret = false;
6265         Roo.each(this.navItems, function(e) {
6266             if (e.tabId == tabId) {
6267                ret =  e;
6268                return false;
6269             }
6270             return true;
6271             
6272         });
6273         return ret;
6274     },
6275     
6276     setActiveNext : function()
6277     {
6278         var i = this.indexOfNav(this.getActive());
6279         if (i > this.navItems.length) {
6280             return;
6281         }
6282         this.setActiveItem(this.navItems[i+1]);
6283     },
6284     setActivePrev : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i  < 1) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i-1]);
6291     },
6292     clearWasActive : function(except) {
6293         Roo.each(this.navItems, function(e) {
6294             if (e.tabId != except.tabId && e.was_active) {
6295                e.was_active = false;
6296                return false;
6297             }
6298             return true;
6299             
6300         });
6301     },
6302     getWasActive : function ()
6303     {
6304         var r = false;
6305         Roo.each(this.navItems, function(e) {
6306             if (e.was_active) {
6307                r = e;
6308                return false;
6309             }
6310             return true;
6311             
6312         });
6313         return r;
6314     }
6315     
6316     
6317 });
6318
6319  
6320 Roo.apply(Roo.bootstrap.nav.Group, {
6321     
6322     groups: {},
6323      /**
6324     * register a Navigation Group
6325     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6326     */
6327     register : function(navgrp)
6328     {
6329         this.groups[navgrp.navId] = navgrp;
6330         
6331     },
6332     /**
6333     * fetch a Navigation Group based on the navigation ID
6334     * @param {string} the navgroup to add
6335     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6336     */
6337     get: function(navId) {
6338         if (typeof(this.groups[navId]) == 'undefined') {
6339             return false;
6340             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6341         }
6342         return this.groups[navId] ;
6343     }
6344     
6345     
6346     
6347 });
6348
6349  /**
6350  * @class Roo.bootstrap.nav.Item
6351  * @extends Roo.bootstrap.Component
6352  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6353  * @parent Roo.bootstrap.nav.Group
6354  * @licence LGPL
6355  * Bootstrap Navbar.NavItem class
6356  * 
6357  * @cfg {String} href  link to
6358  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6359  * @cfg {Boolean} button_outline show and outlined button
6360  * @cfg {String} html content of button
6361  * @cfg {String} badge text inside badge
6362  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6363  * @cfg {String} glyphicon DEPRICATED - use fa
6364  * @cfg {String} icon DEPRICATED - use fa
6365  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6366  * @cfg {Boolean} active Is item active
6367  * @cfg {Boolean} disabled Is item disabled
6368  * @cfg {String} linkcls  Link Class
6369  * @cfg {Boolean} preventDefault (true | false) default false
6370  * @cfg {String} tabId the tab that this item activates.
6371  * @cfg {String} tagtype (a|span) render as a href or span?
6372  * @cfg {Boolean} animateRef (true|false) link to element default false  
6373  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6374   
6375  * @constructor
6376  * Create a new Navbar Item
6377  * @param {Object} config The config object
6378  */
6379 Roo.bootstrap.nav.Item = function(config){
6380     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6381     this.addEvents({
6382         // raw events
6383         /**
6384          * @event click
6385          * The raw click event for the entire grid.
6386          * @param {Roo.EventObject} e
6387          */
6388         "click" : true,
6389          /**
6390             * @event changed
6391             * Fires when the active item active state changes
6392             * @param {Roo.bootstrap.nav.Item} this
6393             * @param {boolean} state the new state
6394              
6395          */
6396         'changed': true,
6397         /**
6398             * @event scrollto
6399             * Fires when scroll to element
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {Object} options
6402             * @param {Roo.EventObject} e
6403              
6404          */
6405         'scrollto': true
6406     });
6407    
6408 };
6409
6410 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6411     
6412     href: false,
6413     html: '',
6414     badge: '',
6415     icon: false,
6416     fa : false,
6417     glyphicon: false,
6418     active: false,
6419     preventDefault : false,
6420     tabId : false,
6421     tagtype : 'a',
6422     tag: 'li',
6423     disabled : false,
6424     animateRef : false,
6425     was_active : false,
6426     button_weight : '',
6427     button_outline : false,
6428     linkcls : '',
6429     navLink: false,
6430     
6431     getAutoCreate : function(){
6432          
6433         var cfg = {
6434             tag: this.tag,
6435             cls: 'nav-item'
6436         };
6437         
6438         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6439         
6440         if (this.active) {
6441             cfg.cls +=  ' active' ;
6442         }
6443         if (this.disabled) {
6444             cfg.cls += ' disabled';
6445         }
6446         
6447         // BS4 only?
6448         if (this.button_weight.length) {
6449             cfg.tag = this.href ? 'a' : 'button';
6450             cfg.html = this.html || '';
6451             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6452             if (this.href) {
6453                 cfg.href = this.href;
6454             }
6455             if (this.fa) {
6456                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6457             } else {
6458                 cfg.cls += " nav-html";
6459             }
6460             
6461             // menu .. should add dropdown-menu class - so no need for carat..
6462             
6463             if (this.badge !== '') {
6464                  
6465                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6466             }
6467             return cfg;
6468         }
6469         
6470         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6471             cfg.cn = [
6472                 {
6473                     tag: this.tagtype,
6474                     href : this.href || "#",
6475                     html: this.html || '',
6476                     cls : ''
6477                 }
6478             ];
6479             if (this.tagtype == 'a') {
6480                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6481         
6482             }
6483             if (this.icon) {
6484                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else  if (this.fa) {
6486                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6487             } else if(this.glyphicon) {
6488                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6489             } else {
6490                 cfg.cn[0].cls += " nav-html";
6491             }
6492             
6493             if (this.menu) {
6494                 cfg.cn[0].html += " <span class='caret'></span>";
6495              
6496             }
6497             
6498             if (this.badge !== '') {
6499                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6500             }
6501         }
6502         
6503         
6504         
6505         return cfg;
6506     },
6507     onRender : function(ct, position)
6508     {
6509        // Roo.log("Call onRender: " + this.xtype);
6510         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6511             this.tag = 'div';
6512         }
6513         
6514         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6515         this.navLink = this.el.select('.nav-link',true).first();
6516         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6517         return ret;
6518     },
6519       
6520     
6521     initEvents: function() 
6522     {
6523         if (typeof (this.menu) != 'undefined') {
6524             this.menu.parentType = this.xtype;
6525             this.menu.triggerEl = this.el;
6526             this.menu = this.addxtype(Roo.apply({}, this.menu));
6527         }
6528         
6529         this.el.on('click', this.onClick, this);
6530         
6531         //if(this.tagtype == 'span'){
6532         //    this.el.select('span',true).on('click', this.onClick, this);
6533         //}
6534        
6535         // at this point parent should be available..
6536         this.parent().register(this);
6537     },
6538     
6539     onClick : function(e)
6540     {
6541         if (e.getTarget('.dropdown-menu-item')) {
6542             // did you click on a menu itemm.... - then don't trigger onclick..
6543             return;
6544         }
6545         
6546         if(
6547                 this.preventDefault || 
6548                 this.href == '#' 
6549         ){
6550             Roo.log("NavItem - prevent Default?");
6551             e.preventDefault();
6552         }
6553         
6554         if (this.disabled) {
6555             return;
6556         }
6557         
6558         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6559         if (tg && tg.transition) {
6560             Roo.log("waiting for the transitionend");
6561             return;
6562         }
6563         
6564         
6565         
6566         //Roo.log("fire event clicked");
6567         if(this.fireEvent('click', this, e) === false){
6568             return;
6569         };
6570         
6571         if(this.tagtype == 'span'){
6572             return;
6573         }
6574         
6575         //Roo.log(this.href);
6576         var ael = this.el.select('a',true).first();
6577         //Roo.log(ael);
6578         
6579         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6580             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6581             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6582                 return; // ignore... - it's a 'hash' to another page.
6583             }
6584             Roo.log("NavItem - prevent Default?");
6585             e.preventDefault();
6586             this.scrollToElement(e);
6587         }
6588         
6589         
6590         var p =  this.parent();
6591    
6592         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6593             if (typeof(p.setActiveItem) !== 'undefined') {
6594                 p.setActiveItem(this);
6595             }
6596         }
6597         
6598         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6599         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6600             // remove the collapsed menu expand...
6601             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6602         }
6603     },
6604     
6605     isActive: function () {
6606         return this.active
6607     },
6608     setActive : function(state, fire, is_was_active)
6609     {
6610         if (this.active && !state && this.navId) {
6611             this.was_active = true;
6612             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6613             if (nv) {
6614                 nv.clearWasActive(this);
6615             }
6616             
6617         }
6618         this.active = state;
6619         
6620         if (!state ) {
6621             this.el.removeClass('active');
6622             this.navLink ? this.navLink.removeClass('active') : false;
6623         } else if (!this.el.hasClass('active')) {
6624             
6625             this.el.addClass('active');
6626             if (Roo.bootstrap.version == 4 && this.navLink ) {
6627                 this.navLink.addClass('active');
6628             }
6629             
6630         }
6631         if (fire) {
6632             this.fireEvent('changed', this, state);
6633         }
6634         
6635         // show a panel if it's registered and related..
6636         
6637         if (!this.navId || !this.tabId || !state || is_was_active) {
6638             return;
6639         }
6640         
6641         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6642         if (!tg) {
6643             return;
6644         }
6645         var pan = tg.getPanelByName(this.tabId);
6646         if (!pan) {
6647             return;
6648         }
6649         // if we can not flip to new panel - go back to old nav highlight..
6650         if (false == tg.showPanel(pan)) {
6651             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6652             if (nv) {
6653                 var onav = nv.getWasActive();
6654                 if (onav) {
6655                     onav.setActive(true, false, true);
6656                 }
6657             }
6658             
6659         }
6660         
6661         
6662         
6663     },
6664      // this should not be here...
6665     setDisabled : function(state)
6666     {
6667         this.disabled = state;
6668         if (!state ) {
6669             this.el.removeClass('disabled');
6670         } else if (!this.el.hasClass('disabled')) {
6671             this.el.addClass('disabled');
6672         }
6673         
6674     },
6675     
6676     /**
6677      * Fetch the element to display the tooltip on.
6678      * @return {Roo.Element} defaults to this.el
6679      */
6680     tooltipEl : function()
6681     {
6682         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6683     },
6684     
6685     scrollToElement : function(e)
6686     {
6687         var c = document.body;
6688         
6689         /*
6690          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6691          */
6692         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6693             c = document.documentElement;
6694         }
6695         
6696         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6697         
6698         if(!target){
6699             return;
6700         }
6701
6702         var o = target.calcOffsetsTo(c);
6703         
6704         var options = {
6705             target : target,
6706             value : o[1]
6707         };
6708         
6709         this.fireEvent('scrollto', this, options, e);
6710         
6711         Roo.get(c).scrollTo('top', options.value, true);
6712         
6713         return;
6714     },
6715     /**
6716      * Set the HTML (text content) of the item
6717      * @param {string} html  content for the nav item
6718      */
6719     setHtml : function(html)
6720     {
6721         this.html = html;
6722         this.htmlEl.dom.innerHTML = html;
6723         
6724     } 
6725 });
6726  
6727
6728  /*
6729  * - LGPL
6730  *
6731  * sidebar item
6732  *
6733  *  li
6734  *    <span> icon </span>
6735  *    <span> text </span>
6736  *    <span>badge </span>
6737  */
6738
6739 /**
6740  * @class Roo.bootstrap.nav.SidebarItem
6741  * @extends Roo.bootstrap.nav.Item
6742  * Bootstrap Navbar.NavSidebarItem class
6743  * 
6744  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6745  * {Boolean} open is the menu open
6746  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6747  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6748  * {String} buttonSize (sm|md|lg)the extra classes for the button
6749  * {Boolean} showArrow show arrow next to the text (default true)
6750  * @constructor
6751  * Create a new Navbar Button
6752  * @param {Object} config The config object
6753  */
6754 Roo.bootstrap.nav.SidebarItem = function(config){
6755     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6756     this.addEvents({
6757         // raw events
6758         /**
6759          * @event click
6760          * The raw click event for the entire grid.
6761          * @param {Roo.EventObject} e
6762          */
6763         "click" : true,
6764          /**
6765             * @event changed
6766             * Fires when the active item active state changes
6767             * @param {Roo.bootstrap.nav.SidebarItem} this
6768             * @param {boolean} state the new state
6769              
6770          */
6771         'changed': true
6772     });
6773    
6774 };
6775
6776 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6777     
6778     badgeWeight : 'default',
6779     
6780     open: false,
6781     
6782     buttonView : false,
6783     
6784     buttonWeight : 'default',
6785     
6786     buttonSize : 'md',
6787     
6788     showArrow : true,
6789     
6790     getAutoCreate : function(){
6791         
6792         
6793         var a = {
6794                 tag: 'a',
6795                 href : this.href || '#',
6796                 cls: '',
6797                 html : '',
6798                 cn : []
6799         };
6800         
6801         if(this.buttonView){
6802             a = {
6803                 tag: 'button',
6804                 href : this.href || '#',
6805                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6806                 html : this.html,
6807                 cn : []
6808             };
6809         }
6810         
6811         var cfg = {
6812             tag: 'li',
6813             cls: '',
6814             cn: [ a ]
6815         };
6816         
6817         if (this.active) {
6818             cfg.cls += ' active';
6819         }
6820         
6821         if (this.disabled) {
6822             cfg.cls += ' disabled';
6823         }
6824         if (this.open) {
6825             cfg.cls += ' open x-open';
6826         }
6827         // left icon..
6828         if (this.glyphicon || this.icon) {
6829             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6830             a.cn.push({ tag : 'i', cls : c }) ;
6831         }
6832         
6833         if(!this.buttonView){
6834             var span = {
6835                 tag: 'span',
6836                 html : this.html || ''
6837             };
6838
6839             a.cn.push(span);
6840             
6841         }
6842         
6843         if (this.badge !== '') {
6844             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6845         }
6846         
6847         if (this.menu) {
6848             
6849             if(this.showArrow){
6850                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6851             }
6852             
6853             a.cls += ' dropdown-toggle treeview' ;
6854         }
6855         
6856         return cfg;
6857     },
6858     
6859     initEvents : function()
6860     { 
6861         if (typeof (this.menu) != 'undefined') {
6862             this.menu.parentType = this.xtype;
6863             this.menu.triggerEl = this.el;
6864             this.menu = this.addxtype(Roo.apply({}, this.menu));
6865         }
6866         
6867         this.el.on('click', this.onClick, this);
6868         
6869         if(this.badge !== ''){
6870             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6871         }
6872         
6873     },
6874     
6875     onClick : function(e)
6876     {
6877         if(this.disabled){
6878             e.preventDefault();
6879             return;
6880         }
6881         
6882         if(this.preventDefault){
6883             e.preventDefault();
6884         }
6885         
6886         this.fireEvent('click', this, e);
6887     },
6888     
6889     disable : function()
6890     {
6891         this.setDisabled(true);
6892     },
6893     
6894     enable : function()
6895     {
6896         this.setDisabled(false);
6897     },
6898     
6899     setDisabled : function(state)
6900     {
6901         if(this.disabled == state){
6902             return;
6903         }
6904         
6905         this.disabled = state;
6906         
6907         if (state) {
6908             this.el.addClass('disabled');
6909             return;
6910         }
6911         
6912         this.el.removeClass('disabled');
6913         
6914         return;
6915     },
6916     
6917     setActive : function(state)
6918     {
6919         if(this.active == state){
6920             return;
6921         }
6922         
6923         this.active = state;
6924         
6925         if (state) {
6926             this.el.addClass('active');
6927             return;
6928         }
6929         
6930         this.el.removeClass('active');
6931         
6932         return;
6933     },
6934     
6935     isActive: function () 
6936     {
6937         return this.active;
6938     },
6939     
6940     setBadge : function(str)
6941     {
6942         if(!this.badgeEl){
6943             return;
6944         }
6945         
6946         this.badgeEl.dom.innerHTML = str;
6947     }
6948     
6949    
6950      
6951  
6952 });
6953  
6954
6955  /*
6956  * - LGPL
6957  *
6958  * nav progress bar
6959  * 
6960  */
6961
6962 /**
6963  * @class Roo.bootstrap.nav.ProgressBar
6964  * @extends Roo.bootstrap.Component
6965  * @children Roo.bootstrap.nav.ProgressBarItem
6966  * Bootstrap NavProgressBar class
6967  * 
6968  * @constructor
6969  * Create a new nav progress bar - a bar indicating step along a process
6970  * @param {Object} config The config object
6971  */
6972
6973 Roo.bootstrap.nav.ProgressBar = function(config){
6974     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6975
6976     this.bullets = this.bullets || [];
6977    
6978 //    Roo.bootstrap.nav.ProgressBar.register(this);
6979      this.addEvents({
6980         /**
6981              * @event changed
6982              * Fires when the active item changes
6983              * @param {Roo.bootstrap.nav.ProgressBar} this
6984              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6985              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6986          */
6987         'changed': true
6988      });
6989     
6990 };
6991
6992 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6993     /**
6994      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6995      * Bullets for the Nav Progress bar for the toolbar
6996      */
6997     bullets : [],
6998     barItems : [],
6999     
7000     getAutoCreate : function()
7001     {
7002         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7003         
7004         cfg = {
7005             tag : 'div',
7006             cls : 'roo-navigation-bar-group',
7007             cn : [
7008                 {
7009                     tag : 'div',
7010                     cls : 'roo-navigation-top-bar'
7011                 },
7012                 {
7013                     tag : 'div',
7014                     cls : 'roo-navigation-bullets-bar',
7015                     cn : [
7016                         {
7017                             tag : 'ul',
7018                             cls : 'roo-navigation-bar'
7019                         }
7020                     ]
7021                 },
7022                 
7023                 {
7024                     tag : 'div',
7025                     cls : 'roo-navigation-bottom-bar'
7026                 }
7027             ]
7028             
7029         };
7030         
7031         return cfg;
7032         
7033     },
7034     
7035     initEvents: function() 
7036     {
7037         
7038     },
7039     
7040     onRender : function(ct, position) 
7041     {
7042         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7043         
7044         if(this.bullets.length){
7045             Roo.each(this.bullets, function(b){
7046                this.addItem(b);
7047             }, this);
7048         }
7049         
7050         this.format();
7051         
7052     },
7053     
7054     addItem : function(cfg)
7055     {
7056         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7057         
7058         item.parentId = this.id;
7059         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7060         
7061         if(cfg.html){
7062             var top = new Roo.bootstrap.Element({
7063                 tag : 'div',
7064                 cls : 'roo-navigation-bar-text'
7065             });
7066             
7067             var bottom = new Roo.bootstrap.Element({
7068                 tag : 'div',
7069                 cls : 'roo-navigation-bar-text'
7070             });
7071             
7072             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7073             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7074             
7075             var topText = new Roo.bootstrap.Element({
7076                 tag : 'span',
7077                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7078             });
7079             
7080             var bottomText = new Roo.bootstrap.Element({
7081                 tag : 'span',
7082                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7083             });
7084             
7085             topText.onRender(top.el, null);
7086             bottomText.onRender(bottom.el, null);
7087             
7088             item.topEl = top;
7089             item.bottomEl = bottom;
7090         }
7091         
7092         this.barItems.push(item);
7093         
7094         return item;
7095     },
7096     
7097     getActive : function()
7098     {
7099         var active = false;
7100         
7101         Roo.each(this.barItems, function(v){
7102             
7103             if (!v.isActive()) {
7104                 return;
7105             }
7106             
7107             active = v;
7108             return false;
7109             
7110         });
7111         
7112         return active;
7113     },
7114     
7115     setActiveItem : function(item)
7116     {
7117         var prev = false;
7118         
7119         Roo.each(this.barItems, function(v){
7120             if (v.rid == item.rid) {
7121                 return ;
7122             }
7123             
7124             if (v.isActive()) {
7125                 v.setActive(false);
7126                 prev = v;
7127             }
7128         });
7129
7130         item.setActive(true);
7131         
7132         this.fireEvent('changed', this, item, prev);
7133     },
7134     
7135     getBarItem: function(rid)
7136     {
7137         var ret = false;
7138         
7139         Roo.each(this.barItems, function(e) {
7140             if (e.rid != rid) {
7141                 return;
7142             }
7143             
7144             ret =  e;
7145             return false;
7146         });
7147         
7148         return ret;
7149     },
7150     
7151     indexOfItem : function(item)
7152     {
7153         var index = false;
7154         
7155         Roo.each(this.barItems, function(v, i){
7156             
7157             if (v.rid != item.rid) {
7158                 return;
7159             }
7160             
7161             index = i;
7162             return false
7163         });
7164         
7165         return index;
7166     },
7167     
7168     setActiveNext : function()
7169     {
7170         var i = this.indexOfItem(this.getActive());
7171         
7172         if (i > this.barItems.length) {
7173             return;
7174         }
7175         
7176         this.setActiveItem(this.barItems[i+1]);
7177     },
7178     
7179     setActivePrev : function()
7180     {
7181         var i = this.indexOfItem(this.getActive());
7182         
7183         if (i  < 1) {
7184             return;
7185         }
7186         
7187         this.setActiveItem(this.barItems[i-1]);
7188     },
7189     
7190     format : function()
7191     {
7192         if(!this.barItems.length){
7193             return;
7194         }
7195      
7196         var width = 100 / this.barItems.length;
7197         
7198         Roo.each(this.barItems, function(i){
7199             i.el.setStyle('width', width + '%');
7200             i.topEl.el.setStyle('width', width + '%');
7201             i.bottomEl.el.setStyle('width', width + '%');
7202         }, this);
7203         
7204     }
7205     
7206 });
7207 /*
7208  * - LGPL
7209  *
7210  * Nav Progress Item
7211  * 
7212  */
7213
7214 /**
7215  * @class Roo.bootstrap.nav.ProgressBarItem
7216  * @extends Roo.bootstrap.Component
7217  * Bootstrap NavProgressBarItem class
7218  * @cfg {String} rid the reference id
7219  * @cfg {Boolean} active (true|false) Is item active default false
7220  * @cfg {Boolean} disabled (true|false) Is item active default false
7221  * @cfg {String} html
7222  * @cfg {String} position (top|bottom) text position default bottom
7223  * @cfg {String} icon show icon instead of number
7224  * 
7225  * @constructor
7226  * Create a new NavProgressBarItem
7227  * @param {Object} config The config object
7228  */
7229 Roo.bootstrap.nav.ProgressBarItem = function(config){
7230     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7231     this.addEvents({
7232         // raw events
7233         /**
7234          * @event click
7235          * The raw click event for the entire grid.
7236          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7237          * @param {Roo.EventObject} e
7238          */
7239         "click" : true
7240     });
7241    
7242 };
7243
7244 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7245     
7246     rid : '',
7247     active : false,
7248     disabled : false,
7249     html : '',
7250     position : 'bottom',
7251     icon : false,
7252     
7253     getAutoCreate : function()
7254     {
7255         var iconCls = 'roo-navigation-bar-item-icon';
7256         
7257         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7258         
7259         var cfg = {
7260             tag: 'li',
7261             cls: 'roo-navigation-bar-item',
7262             cn : [
7263                 {
7264                     tag : 'i',
7265                     cls : iconCls
7266                 }
7267             ]
7268         };
7269         
7270         if(this.active){
7271             cfg.cls += ' active';
7272         }
7273         if(this.disabled){
7274             cfg.cls += ' disabled';
7275         }
7276         
7277         return cfg;
7278     },
7279     
7280     disable : function()
7281     {
7282         this.setDisabled(true);
7283     },
7284     
7285     enable : function()
7286     {
7287         this.setDisabled(false);
7288     },
7289     
7290     initEvents: function() 
7291     {
7292         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7293         
7294         this.iconEl.on('click', this.onClick, this);
7295     },
7296     
7297     onClick : function(e)
7298     {
7299         e.preventDefault();
7300         
7301         if(this.disabled){
7302             return;
7303         }
7304         
7305         if(this.fireEvent('click', this, e) === false){
7306             return;
7307         };
7308         
7309         this.parent().setActiveItem(this);
7310     },
7311     
7312     isActive: function () 
7313     {
7314         return this.active;
7315     },
7316     
7317     setActive : function(state)
7318     {
7319         if(this.active == state){
7320             return;
7321         }
7322         
7323         this.active = state;
7324         
7325         if (state) {
7326             this.el.addClass('active');
7327             return;
7328         }
7329         
7330         this.el.removeClass('active');
7331         
7332         return;
7333     },
7334     
7335     setDisabled : function(state)
7336     {
7337         if(this.disabled == state){
7338             return;
7339         }
7340         
7341         this.disabled = state;
7342         
7343         if (state) {
7344             this.el.addClass('disabled');
7345             return;
7346         }
7347         
7348         this.el.removeClass('disabled');
7349     },
7350     
7351     tooltipEl : function()
7352     {
7353         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7354     }
7355 });
7356  
7357
7358  /*
7359  * - LGPL
7360  *
7361  *  Breadcrumb Nav
7362  * 
7363  */
7364 Roo.namespace('Roo.bootstrap.breadcrumb');
7365
7366
7367 /**
7368  * @class Roo.bootstrap.breadcrumb.Nav
7369  * @extends Roo.bootstrap.Component
7370  * Bootstrap Breadcrumb Nav Class
7371  *  
7372  * @children Roo.bootstrap.breadcrumb.Item
7373  * 
7374  * @constructor
7375  * Create a new breadcrumb.Nav
7376  * @param {Object} config The config object
7377  */
7378
7379
7380 Roo.bootstrap.breadcrumb.Nav = function(config){
7381     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7382     
7383     
7384 };
7385
7386 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7387     
7388     getAutoCreate : function()
7389     {
7390
7391         var cfg = {
7392             tag: 'nav',
7393             cn : [
7394                 {
7395                     tag : 'ol',
7396                     cls : 'breadcrumb'
7397                 }
7398             ]
7399             
7400         };
7401           
7402         return cfg;
7403     },
7404     
7405     initEvents: function()
7406     {
7407         this.olEl = this.el.select('ol',true).first();    
7408     },
7409     getChildContainer : function()
7410     {
7411         return this.olEl;  
7412     }
7413     
7414 });
7415
7416  /*
7417  * - LGPL
7418  *
7419  *  Breadcrumb Item
7420  * 
7421  */
7422
7423
7424 /**
7425  * @class Roo.bootstrap.breadcrumb.Nav
7426  * @extends Roo.bootstrap.Component
7427  * @children Roo.bootstrap.Component
7428  * @parent Roo.bootstrap.breadcrumb.Nav
7429  * Bootstrap Breadcrumb Nav Class
7430  *  
7431  * 
7432  * @cfg {String} html the content of the link.
7433  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7434  * @cfg {Boolean} active is it active
7435
7436  * 
7437  * @constructor
7438  * Create a new breadcrumb.Nav
7439  * @param {Object} config The config object
7440  */
7441
7442 Roo.bootstrap.breadcrumb.Item = function(config){
7443     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7444     this.addEvents({
7445         // img events
7446         /**
7447          * @event click
7448          * The img click event for the img.
7449          * @param {Roo.EventObject} e
7450          */
7451         "click" : true
7452     });
7453     
7454 };
7455
7456 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7457     
7458     href: false,
7459     html : '',
7460     
7461     getAutoCreate : function()
7462     {
7463
7464         var cfg = {
7465             tag: 'li',
7466             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7467         };
7468         if (this.href !== false) {
7469             cfg.cn = [{
7470                 tag : 'a',
7471                 href : this.href,
7472                 html : this.html
7473             }];
7474         } else {
7475             cfg.html = this.html;
7476         }
7477         
7478         return cfg;
7479     },
7480     
7481     initEvents: function()
7482     {
7483         if (this.href) {
7484             this.el.select('a', true).first().on('click',this.onClick, this)
7485         }
7486         
7487     },
7488     onClick : function(e)
7489     {
7490         e.preventDefault();
7491         this.fireEvent('click',this,  e);
7492     }
7493     
7494 });
7495
7496  /*
7497  * - LGPL
7498  *
7499  * row
7500  * 
7501  */
7502
7503 /**
7504  * @class Roo.bootstrap.Row
7505  * @extends Roo.bootstrap.Component
7506  * @children Roo.bootstrap.Component
7507  * Bootstrap Row class (contains columns...)
7508  * 
7509  * @constructor
7510  * Create a new Row
7511  * @param {Object} config The config object
7512  */
7513
7514 Roo.bootstrap.Row = function(config){
7515     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7516 };
7517
7518 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7519     
7520     getAutoCreate : function(){
7521        return {
7522             cls: 'row clearfix'
7523        };
7524     }
7525     
7526     
7527 });
7528
7529  
7530
7531  /*
7532  * - LGPL
7533  *
7534  * pagination
7535  * 
7536  */
7537
7538 /**
7539  * @class Roo.bootstrap.Pagination
7540  * @extends Roo.bootstrap.Component
7541  * @children Roo.bootstrap.Pagination
7542  * Bootstrap Pagination class
7543  * 
7544  * @cfg {String} size (xs|sm|md|lg|xl)
7545  * @cfg {Boolean} inverse 
7546  * 
7547  * @constructor
7548  * Create a new Pagination
7549  * @param {Object} config The config object
7550  */
7551
7552 Roo.bootstrap.Pagination = function(config){
7553     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7554 };
7555
7556 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7557     
7558     cls: false,
7559     size: false,
7560     inverse: false,
7561     
7562     getAutoCreate : function(){
7563         var cfg = {
7564             tag: 'ul',
7565                 cls: 'pagination'
7566         };
7567         if (this.inverse) {
7568             cfg.cls += ' inverse';
7569         }
7570         if (this.html) {
7571             cfg.html=this.html;
7572         }
7573         if (this.cls) {
7574             cfg.cls += " " + this.cls;
7575         }
7576         return cfg;
7577     }
7578    
7579 });
7580
7581  
7582
7583  /*
7584  * - LGPL
7585  *
7586  * Pagination item
7587  * 
7588  */
7589
7590
7591 /**
7592  * @class Roo.bootstrap.PaginationItem
7593  * @extends Roo.bootstrap.Component
7594  * Bootstrap PaginationItem class
7595  * @cfg {String} html text
7596  * @cfg {String} href the link
7597  * @cfg {Boolean} preventDefault (true | false) default true
7598  * @cfg {Boolean} active (true | false) default false
7599  * @cfg {Boolean} disabled default false
7600  * 
7601  * 
7602  * @constructor
7603  * Create a new PaginationItem
7604  * @param {Object} config The config object
7605  */
7606
7607
7608 Roo.bootstrap.PaginationItem = function(config){
7609     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7610     this.addEvents({
7611         // raw events
7612         /**
7613          * @event click
7614          * The raw click event for the entire grid.
7615          * @param {Roo.EventObject} e
7616          */
7617         "click" : true
7618     });
7619 };
7620
7621 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7622     
7623     href : false,
7624     html : false,
7625     preventDefault: true,
7626     active : false,
7627     cls : false,
7628     disabled: false,
7629     
7630     getAutoCreate : function(){
7631         var cfg= {
7632             tag: 'li',
7633             cn: [
7634                 {
7635                     tag : 'a',
7636                     href : this.href ? this.href : '#',
7637                     html : this.html ? this.html : ''
7638                 }
7639             ]
7640         };
7641         
7642         if(this.cls){
7643             cfg.cls = this.cls;
7644         }
7645         
7646         if(this.disabled){
7647             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7648         }
7649         
7650         if(this.active){
7651             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7652         }
7653         
7654         return cfg;
7655     },
7656     
7657     initEvents: function() {
7658         
7659         this.el.on('click', this.onClick, this);
7660         
7661     },
7662     onClick : function(e)
7663     {
7664         Roo.log('PaginationItem on click ');
7665         if(this.preventDefault){
7666             e.preventDefault();
7667         }
7668         
7669         if(this.disabled){
7670             return;
7671         }
7672         
7673         this.fireEvent('click', this, e);
7674     }
7675    
7676 });
7677
7678  
7679
7680  /*
7681  * - LGPL
7682  *
7683  * slider
7684  * 
7685  */
7686
7687
7688 /**
7689  * @class Roo.bootstrap.Slider
7690  * @extends Roo.bootstrap.Component
7691  * Bootstrap Slider class
7692  *    
7693  * @constructor
7694  * Create a new Slider
7695  * @param {Object} config The config object
7696  */
7697
7698 Roo.bootstrap.Slider = function(config){
7699     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7700 };
7701
7702 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7703     
7704     getAutoCreate : function(){
7705         
7706         var cfg = {
7707             tag: 'div',
7708             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7709             cn: [
7710                 {
7711                     tag: 'a',
7712                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7713                 }
7714             ]
7715         };
7716         
7717         return cfg;
7718     }
7719    
7720 });
7721
7722  /*
7723  * Based on:
7724  * Ext JS Library 1.1.1
7725  * Copyright(c) 2006-2007, Ext JS, LLC.
7726  *
7727  * Originally Released Under LGPL - original licence link has changed is not relivant.
7728  *
7729  * Fork - LGPL
7730  * <script type="text/javascript">
7731  */
7732  /**
7733  * @extends Roo.dd.DDProxy
7734  * @class Roo.grid.SplitDragZone
7735  * Support for Column Header resizing
7736  * @constructor
7737  * @param {Object} config
7738  */
7739 // private
7740 // This is a support class used internally by the Grid components
7741 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7742     this.grid = grid;
7743     this.view = grid.getView();
7744     this.proxy = this.view.resizeProxy;
7745     Roo.grid.SplitDragZone.superclass.constructor.call(
7746         this,
7747         hd, // ID
7748         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7749         {  // CONFIG
7750             dragElId : Roo.id(this.proxy.dom),
7751             resizeFrame:false
7752         }
7753     );
7754     
7755     this.setHandleElId(Roo.id(hd));
7756     if (hd2 !== false) {
7757         this.setOuterHandleElId(Roo.id(hd2));
7758     }
7759     
7760     this.scroll = false;
7761 };
7762 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7763     fly: Roo.Element.fly,
7764
7765     b4StartDrag : function(x, y){
7766         this.view.headersDisabled = true;
7767         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7768                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7769         );
7770         this.proxy.setHeight(h);
7771         
7772         // for old system colWidth really stored the actual width?
7773         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7774         // which in reality did not work.. - it worked only for fixed sizes
7775         // for resizable we need to use actual sizes.
7776         var w = this.cm.getColumnWidth(this.cellIndex);
7777         if (!this.view.mainWrap) {
7778             // bootstrap.
7779             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7780         }
7781         
7782         
7783         
7784         // this was w-this.grid.minColumnWidth;
7785         // doesnt really make sense? - w = thie curren width or the rendered one?
7786         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7787         this.resetConstraints();
7788         this.setXConstraint(minw, 1000);
7789         this.setYConstraint(0, 0);
7790         this.minX = x - minw;
7791         this.maxX = x + 1000;
7792         this.startPos = x;
7793         if (!this.view.mainWrap) { // this is Bootstrap code..
7794             this.getDragEl().style.display='block';
7795         }
7796         
7797         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7798     },
7799
7800
7801     handleMouseDown : function(e){
7802         ev = Roo.EventObject.setEvent(e);
7803         var t = this.fly(ev.getTarget());
7804         if(t.hasClass("x-grid-split")){
7805             this.cellIndex = this.view.getCellIndex(t.dom);
7806             this.split = t.dom;
7807             this.cm = this.grid.colModel;
7808             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7809                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7810             }
7811         }
7812     },
7813
7814     endDrag : function(e){
7815         this.view.headersDisabled = false;
7816         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7817         var diff = endX - this.startPos;
7818         // 
7819         var w = this.cm.getColumnWidth(this.cellIndex);
7820         if (!this.view.mainWrap) {
7821             w = 0;
7822         }
7823         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7824     },
7825
7826     autoOffset : function(){
7827         this.setDelta(0,0);
7828     }
7829 });/*
7830  * Based on:
7831  * Ext JS Library 1.1.1
7832  * Copyright(c) 2006-2007, Ext JS, LLC.
7833  *
7834  * Originally Released Under LGPL - original licence link has changed is not relivant.
7835  *
7836  * Fork - LGPL
7837  * <script type="text/javascript">
7838  */
7839
7840 /**
7841  * @class Roo.grid.AbstractSelectionModel
7842  * @extends Roo.util.Observable
7843  * @abstract
7844  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7845  * implemented by descendant classes.  This class should not be directly instantiated.
7846  * @constructor
7847  */
7848 Roo.grid.AbstractSelectionModel = function(){
7849     this.locked = false;
7850     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7851 };
7852
7853 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7854     /** @ignore Called by the grid automatically. Do not call directly. */
7855     init : function(grid){
7856         this.grid = grid;
7857         this.initEvents();
7858     },
7859
7860     /**
7861      * Locks the selections.
7862      */
7863     lock : function(){
7864         this.locked = true;
7865     },
7866
7867     /**
7868      * Unlocks the selections.
7869      */
7870     unlock : function(){
7871         this.locked = false;
7872     },
7873
7874     /**
7875      * Returns true if the selections are locked.
7876      * @return {Boolean}
7877      */
7878     isLocked : function(){
7879         return this.locked;
7880     }
7881 });/*
7882  * Based on:
7883  * Ext JS Library 1.1.1
7884  * Copyright(c) 2006-2007, Ext JS, LLC.
7885  *
7886  * Originally Released Under LGPL - original licence link has changed is not relivant.
7887  *
7888  * Fork - LGPL
7889  * <script type="text/javascript">
7890  */
7891 /**
7892  * @extends Roo.grid.AbstractSelectionModel
7893  * @class Roo.grid.RowSelectionModel
7894  * The default SelectionModel used by {@link Roo.grid.Grid}.
7895  * It supports multiple selections and keyboard selection/navigation. 
7896  * @constructor
7897  * @param {Object} config
7898  */
7899 Roo.grid.RowSelectionModel = function(config){
7900     Roo.apply(this, config);
7901     this.selections = new Roo.util.MixedCollection(false, function(o){
7902         return o.id;
7903     });
7904
7905     this.last = false;
7906     this.lastActive = false;
7907
7908     this.addEvents({
7909         /**
7910         * @event selectionchange
7911         * Fires when the selection changes
7912         * @param {SelectionModel} this
7913         */
7914        "selectionchange" : true,
7915        /**
7916         * @event afterselectionchange
7917         * Fires after the selection changes (eg. by key press or clicking)
7918         * @param {SelectionModel} this
7919         */
7920        "afterselectionchange" : true,
7921        /**
7922         * @event beforerowselect
7923         * Fires when a row is selected being selected, return false to cancel.
7924         * @param {SelectionModel} this
7925         * @param {Number} rowIndex The selected index
7926         * @param {Boolean} keepExisting False if other selections will be cleared
7927         */
7928        "beforerowselect" : true,
7929        /**
7930         * @event rowselect
7931         * Fires when a row is selected.
7932         * @param {SelectionModel} this
7933         * @param {Number} rowIndex The selected index
7934         * @param {Roo.data.Record} r The record
7935         */
7936        "rowselect" : true,
7937        /**
7938         * @event rowdeselect
7939         * Fires when a row is deselected.
7940         * @param {SelectionModel} this
7941         * @param {Number} rowIndex The selected index
7942         */
7943         "rowdeselect" : true
7944     });
7945     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7946     this.locked = false;
7947 };
7948
7949 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7950     /**
7951      * @cfg {Boolean} singleSelect
7952      * True to allow selection of only one row at a time (defaults to false)
7953      */
7954     singleSelect : false,
7955
7956     // private
7957     initEvents : function(){
7958
7959         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7960             this.grid.on("mousedown", this.handleMouseDown, this);
7961         }else{ // allow click to work like normal
7962             this.grid.on("rowclick", this.handleDragableRowClick, this);
7963         }
7964         // bootstrap does not have a view..
7965         var view = this.grid.view ? this.grid.view : this.grid;
7966         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7967             "up" : function(e){
7968                 if(!e.shiftKey){
7969                     this.selectPrevious(e.shiftKey);
7970                 }else if(this.last !== false && this.lastActive !== false){
7971                     var last = this.last;
7972                     this.selectRange(this.last,  this.lastActive-1);
7973                     view.focusRow(this.lastActive);
7974                     if(last !== false){
7975                         this.last = last;
7976                     }
7977                 }else{
7978                     this.selectFirstRow();
7979                 }
7980                 this.fireEvent("afterselectionchange", this);
7981             },
7982             "down" : function(e){
7983                 if(!e.shiftKey){
7984                     this.selectNext(e.shiftKey);
7985                 }else if(this.last !== false && this.lastActive !== false){
7986                     var last = this.last;
7987                     this.selectRange(this.last,  this.lastActive+1);
7988                     view.focusRow(this.lastActive);
7989                     if(last !== false){
7990                         this.last = last;
7991                     }
7992                 }else{
7993                     this.selectFirstRow();
7994                 }
7995                 this.fireEvent("afterselectionchange", this);
7996             },
7997             scope: this
7998         });
7999
8000          
8001         view.on("refresh", this.onRefresh, this);
8002         view.on("rowupdated", this.onRowUpdated, this);
8003         view.on("rowremoved", this.onRemove, this);
8004     },
8005
8006     // private
8007     onRefresh : function(){
8008         var ds = this.grid.ds, i, v = this.grid.view;
8009         var s = this.selections;
8010         s.each(function(r){
8011             if((i = ds.indexOfId(r.id)) != -1){
8012                 v.onRowSelect(i);
8013                 s.add(ds.getAt(i)); // updating the selection relate data
8014             }else{
8015                 s.remove(r);
8016             }
8017         });
8018     },
8019
8020     // private
8021     onRemove : function(v, index, r){
8022         this.selections.remove(r);
8023     },
8024
8025     // private
8026     onRowUpdated : function(v, index, r){
8027         if(this.isSelected(r)){
8028             v.onRowSelect(index);
8029         }
8030     },
8031
8032     /**
8033      * Select records.
8034      * @param {Array} records The records to select
8035      * @param {Boolean} keepExisting (optional) True to keep existing selections
8036      */
8037     selectRecords : function(records, keepExisting){
8038         if(!keepExisting){
8039             this.clearSelections();
8040         }
8041         var ds = this.grid.ds;
8042         for(var i = 0, len = records.length; i < len; i++){
8043             this.selectRow(ds.indexOf(records[i]), true);
8044         }
8045     },
8046
8047     /**
8048      * Gets the number of selected rows.
8049      * @return {Number}
8050      */
8051     getCount : function(){
8052         return this.selections.length;
8053     },
8054
8055     /**
8056      * Selects the first row in the grid.
8057      */
8058     selectFirstRow : function(){
8059         this.selectRow(0);
8060     },
8061
8062     /**
8063      * Select the last row.
8064      * @param {Boolean} keepExisting (optional) True to keep existing selections
8065      */
8066     selectLastRow : function(keepExisting){
8067         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8068     },
8069
8070     /**
8071      * Selects the row immediately following the last selected row.
8072      * @param {Boolean} keepExisting (optional) True to keep existing selections
8073      */
8074     selectNext : function(keepExisting){
8075         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8076             this.selectRow(this.last+1, keepExisting);
8077             var view = this.grid.view ? this.grid.view : this.grid;
8078             view.focusRow(this.last);
8079         }
8080     },
8081
8082     /**
8083      * Selects the row that precedes the last selected row.
8084      * @param {Boolean} keepExisting (optional) True to keep existing selections
8085      */
8086     selectPrevious : function(keepExisting){
8087         if(this.last){
8088             this.selectRow(this.last-1, keepExisting);
8089             var view = this.grid.view ? this.grid.view : this.grid;
8090             view.focusRow(this.last);
8091         }
8092     },
8093
8094     /**
8095      * Returns the selected records
8096      * @return {Array} Array of selected records
8097      */
8098     getSelections : function(){
8099         return [].concat(this.selections.items);
8100     },
8101
8102     /**
8103      * Returns the first selected record.
8104      * @return {Record}
8105      */
8106     getSelected : function(){
8107         return this.selections.itemAt(0);
8108     },
8109
8110
8111     /**
8112      * Clears all selections.
8113      */
8114     clearSelections : function(fast){
8115         if(this.locked) {
8116             return;
8117         }
8118         if(fast !== true){
8119             var ds = this.grid.ds;
8120             var s = this.selections;
8121             s.each(function(r){
8122                 this.deselectRow(ds.indexOfId(r.id));
8123             }, this);
8124             s.clear();
8125         }else{
8126             this.selections.clear();
8127         }
8128         this.last = false;
8129     },
8130
8131
8132     /**
8133      * Selects all rows.
8134      */
8135     selectAll : function(){
8136         if(this.locked) {
8137             return;
8138         }
8139         this.selections.clear();
8140         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8141             this.selectRow(i, true);
8142         }
8143     },
8144
8145     /**
8146      * Returns True if there is a selection.
8147      * @return {Boolean}
8148      */
8149     hasSelection : function(){
8150         return this.selections.length > 0;
8151     },
8152
8153     /**
8154      * Returns True if the specified row is selected.
8155      * @param {Number/Record} record The record or index of the record to check
8156      * @return {Boolean}
8157      */
8158     isSelected : function(index){
8159         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8160         return (r && this.selections.key(r.id) ? true : false);
8161     },
8162
8163     /**
8164      * Returns True if the specified record id is selected.
8165      * @param {String} id The id of record to check
8166      * @return {Boolean}
8167      */
8168     isIdSelected : function(id){
8169         return (this.selections.key(id) ? true : false);
8170     },
8171
8172     // private
8173     handleMouseDown : function(e, t)
8174     {
8175         var view = this.grid.view ? this.grid.view : this.grid;
8176         var rowIndex;
8177         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8178             return;
8179         };
8180         if(e.shiftKey && this.last !== false){
8181             var last = this.last;
8182             this.selectRange(last, rowIndex, e.ctrlKey);
8183             this.last = last; // reset the last
8184             view.focusRow(rowIndex);
8185         }else{
8186             var isSelected = this.isSelected(rowIndex);
8187             if(e.button !== 0 && isSelected){
8188                 view.focusRow(rowIndex);
8189             }else if(e.ctrlKey && isSelected){
8190                 this.deselectRow(rowIndex);
8191             }else if(!isSelected){
8192                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8193                 view.focusRow(rowIndex);
8194             }
8195         }
8196         this.fireEvent("afterselectionchange", this);
8197     },
8198     // private
8199     handleDragableRowClick :  function(grid, rowIndex, e) 
8200     {
8201         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8202             this.selectRow(rowIndex, false);
8203             var view = this.grid.view ? this.grid.view : this.grid;
8204             view.focusRow(rowIndex);
8205              this.fireEvent("afterselectionchange", this);
8206         }
8207     },
8208     
8209     /**
8210      * Selects multiple rows.
8211      * @param {Array} rows Array of the indexes of the row to select
8212      * @param {Boolean} keepExisting (optional) True to keep existing selections
8213      */
8214     selectRows : function(rows, keepExisting){
8215         if(!keepExisting){
8216             this.clearSelections();
8217         }
8218         for(var i = 0, len = rows.length; i < len; i++){
8219             this.selectRow(rows[i], true);
8220         }
8221     },
8222
8223     /**
8224      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8225      * @param {Number} startRow The index of the first row in the range
8226      * @param {Number} endRow The index of the last row in the range
8227      * @param {Boolean} keepExisting (optional) True to retain existing selections
8228      */
8229     selectRange : function(startRow, endRow, keepExisting){
8230         if(this.locked) {
8231             return;
8232         }
8233         if(!keepExisting){
8234             this.clearSelections();
8235         }
8236         if(startRow <= endRow){
8237             for(var i = startRow; i <= endRow; i++){
8238                 this.selectRow(i, true);
8239             }
8240         }else{
8241             for(var i = startRow; i >= endRow; i--){
8242                 this.selectRow(i, true);
8243             }
8244         }
8245     },
8246
8247     /**
8248      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8249      * @param {Number} startRow The index of the first row in the range
8250      * @param {Number} endRow The index of the last row in the range
8251      */
8252     deselectRange : function(startRow, endRow, preventViewNotify){
8253         if(this.locked) {
8254             return;
8255         }
8256         for(var i = startRow; i <= endRow; i++){
8257             this.deselectRow(i, preventViewNotify);
8258         }
8259     },
8260
8261     /**
8262      * Selects a row.
8263      * @param {Number} row The index of the row to select
8264      * @param {Boolean} keepExisting (optional) True to keep existing selections
8265      */
8266     selectRow : function(index, keepExisting, preventViewNotify){
8267         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8268             return;
8269         }
8270         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8271             if(!keepExisting || this.singleSelect){
8272                 this.clearSelections();
8273             }
8274             var r = this.grid.ds.getAt(index);
8275             this.selections.add(r);
8276             this.last = this.lastActive = index;
8277             if(!preventViewNotify){
8278                 var view = this.grid.view ? this.grid.view : this.grid;
8279                 view.onRowSelect(index);
8280             }
8281             this.fireEvent("rowselect", this, index, r);
8282             this.fireEvent("selectionchange", this);
8283         }
8284     },
8285
8286     /**
8287      * Deselects a row.
8288      * @param {Number} row The index of the row to deselect
8289      */
8290     deselectRow : function(index, preventViewNotify){
8291         if(this.locked) {
8292             return;
8293         }
8294         if(this.last == index){
8295             this.last = false;
8296         }
8297         if(this.lastActive == index){
8298             this.lastActive = false;
8299         }
8300         var r = this.grid.ds.getAt(index);
8301         this.selections.remove(r);
8302         if(!preventViewNotify){
8303             var view = this.grid.view ? this.grid.view : this.grid;
8304             view.onRowDeselect(index);
8305         }
8306         this.fireEvent("rowdeselect", this, index);
8307         this.fireEvent("selectionchange", this);
8308     },
8309
8310     // private
8311     restoreLast : function(){
8312         if(this._last){
8313             this.last = this._last;
8314         }
8315     },
8316
8317     // private
8318     acceptsNav : function(row, col, cm){
8319         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8320     },
8321
8322     // private
8323     onEditorKey : function(field, e){
8324         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8325         if(k == e.TAB){
8326             e.stopEvent();
8327             ed.completeEdit();
8328             if(e.shiftKey){
8329                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8330             }else{
8331                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8332             }
8333         }else if(k == e.ENTER && !e.ctrlKey){
8334             e.stopEvent();
8335             ed.completeEdit();
8336             if(e.shiftKey){
8337                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8338             }else{
8339                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8340             }
8341         }else if(k == e.ESC){
8342             ed.cancelEdit();
8343         }
8344         if(newCell){
8345             g.startEditing(newCell[0], newCell[1]);
8346         }
8347     }
8348 });/*
8349  * Based on:
8350  * Ext JS Library 1.1.1
8351  * Copyright(c) 2006-2007, Ext JS, LLC.
8352  *
8353  * Originally Released Under LGPL - original licence link has changed is not relivant.
8354  *
8355  * Fork - LGPL
8356  * <script type="text/javascript">
8357  */
8358  
8359
8360 /**
8361  * @class Roo.grid.ColumnModel
8362  * @extends Roo.util.Observable
8363  * This is the default implementation of a ColumnModel used by the Grid. It defines
8364  * the columns in the grid.
8365  * <br>Usage:<br>
8366  <pre><code>
8367  var colModel = new Roo.grid.ColumnModel([
8368         {header: "Ticker", width: 60, sortable: true, locked: true},
8369         {header: "Company Name", width: 150, sortable: true},
8370         {header: "Market Cap.", width: 100, sortable: true},
8371         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8372         {header: "Employees", width: 100, sortable: true, resizable: false}
8373  ]);
8374  </code></pre>
8375  * <p>
8376  
8377  * The config options listed for this class are options which may appear in each
8378  * individual column definition.
8379  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8380  * @constructor
8381  * @param {Object} config An Array of column config objects. See this class's
8382  * config objects for details.
8383 */
8384 Roo.grid.ColumnModel = function(config){
8385         /**
8386      * The config passed into the constructor
8387      */
8388     this.config = []; //config;
8389     this.lookup = {};
8390
8391     // if no id, create one
8392     // if the column does not have a dataIndex mapping,
8393     // map it to the order it is in the config
8394     for(var i = 0, len = config.length; i < len; i++){
8395         this.addColumn(config[i]);
8396         
8397     }
8398
8399     /**
8400      * The width of columns which have no width specified (defaults to 100)
8401      * @type Number
8402      */
8403     this.defaultWidth = 100;
8404
8405     /**
8406      * Default sortable of columns which have no sortable specified (defaults to false)
8407      * @type Boolean
8408      */
8409     this.defaultSortable = false;
8410
8411     this.addEvents({
8412         /**
8413              * @event widthchange
8414              * Fires when the width of a column changes.
8415              * @param {ColumnModel} this
8416              * @param {Number} columnIndex The column index
8417              * @param {Number} newWidth The new width
8418              */
8419             "widthchange": true,
8420         /**
8421              * @event headerchange
8422              * Fires when the text of a header changes.
8423              * @param {ColumnModel} this
8424              * @param {Number} columnIndex The column index
8425              * @param {Number} newText The new header text
8426              */
8427             "headerchange": true,
8428         /**
8429              * @event hiddenchange
8430              * Fires when a column is hidden or "unhidden".
8431              * @param {ColumnModel} this
8432              * @param {Number} columnIndex The column index
8433              * @param {Boolean} hidden true if hidden, false otherwise
8434              */
8435             "hiddenchange": true,
8436             /**
8437          * @event columnmoved
8438          * Fires when a column is moved.
8439          * @param {ColumnModel} this
8440          * @param {Number} oldIndex
8441          * @param {Number} newIndex
8442          */
8443         "columnmoved" : true,
8444         /**
8445          * @event columlockchange
8446          * Fires when a column's locked state is changed
8447          * @param {ColumnModel} this
8448          * @param {Number} colIndex
8449          * @param {Boolean} locked true if locked
8450          */
8451         "columnlockchange" : true
8452     });
8453     Roo.grid.ColumnModel.superclass.constructor.call(this);
8454 };
8455 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8456     /**
8457      * @cfg {String} header The header text to display in the Grid view.
8458      */
8459         /**
8460      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8461      */
8462         /**
8463      * @cfg {String} smHeader Header at Bootsrap Small width
8464      */
8465         /**
8466      * @cfg {String} mdHeader Header at Bootsrap Medium width
8467      */
8468         /**
8469      * @cfg {String} lgHeader Header at Bootsrap Large width
8470      */
8471         /**
8472      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8473      */
8474     /**
8475      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8476      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8477      * specified, the column's index is used as an index into the Record's data Array.
8478      */
8479     /**
8480      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8481      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8482      */
8483     /**
8484      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8485      * Defaults to the value of the {@link #defaultSortable} property.
8486      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8487      */
8488     /**
8489      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8490      */
8491     /**
8492      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8493      */
8494     /**
8495      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8496      */
8497     /**
8498      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8499      */
8500     /**
8501      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8502      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8503      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8504      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8505      */
8506        /**
8507      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8508      */
8509     /**
8510      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8511      */
8512     /**
8513      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8514      */
8515     /**
8516      * @cfg {String} cursor (Optional)
8517      */
8518     /**
8519      * @cfg {String} tooltip (Optional)
8520      */
8521     /**
8522      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8523      */
8524     /**
8525      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8526      */
8527     /**
8528      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8529      */
8530     /**
8531      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8532      */
8533         /**
8534      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * Returns the id of the column at the specified index.
8538      * @param {Number} index The column index
8539      * @return {String} the id
8540      */
8541     getColumnId : function(index){
8542         return this.config[index].id;
8543     },
8544
8545     /**
8546      * Returns the column for a specified id.
8547      * @param {String} id The column id
8548      * @return {Object} the column
8549      */
8550     getColumnById : function(id){
8551         return this.lookup[id];
8552     },
8553
8554     
8555     /**
8556      * Returns the column Object for a specified dataIndex.
8557      * @param {String} dataIndex The column dataIndex
8558      * @return {Object|Boolean} the column or false if not found
8559      */
8560     getColumnByDataIndex: function(dataIndex){
8561         var index = this.findColumnIndex(dataIndex);
8562         return index > -1 ? this.config[index] : false;
8563     },
8564     
8565     /**
8566      * Returns the index for a specified column id.
8567      * @param {String} id The column id
8568      * @return {Number} the index, or -1 if not found
8569      */
8570     getIndexById : function(id){
8571         for(var i = 0, len = this.config.length; i < len; i++){
8572             if(this.config[i].id == id){
8573                 return i;
8574             }
8575         }
8576         return -1;
8577     },
8578     
8579     /**
8580      * Returns the index for a specified column dataIndex.
8581      * @param {String} dataIndex The column dataIndex
8582      * @return {Number} the index, or -1 if not found
8583      */
8584     
8585     findColumnIndex : function(dataIndex){
8586         for(var i = 0, len = this.config.length; i < len; i++){
8587             if(this.config[i].dataIndex == dataIndex){
8588                 return i;
8589             }
8590         }
8591         return -1;
8592     },
8593     
8594     
8595     moveColumn : function(oldIndex, newIndex){
8596         var c = this.config[oldIndex];
8597         this.config.splice(oldIndex, 1);
8598         this.config.splice(newIndex, 0, c);
8599         this.dataMap = null;
8600         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8601     },
8602
8603     isLocked : function(colIndex){
8604         return this.config[colIndex].locked === true;
8605     },
8606
8607     setLocked : function(colIndex, value, suppressEvent){
8608         if(this.isLocked(colIndex) == value){
8609             return;
8610         }
8611         this.config[colIndex].locked = value;
8612         if(!suppressEvent){
8613             this.fireEvent("columnlockchange", this, colIndex, value);
8614         }
8615     },
8616
8617     getTotalLockedWidth : function(){
8618         var totalWidth = 0;
8619         for(var i = 0; i < this.config.length; i++){
8620             if(this.isLocked(i) && !this.isHidden(i)){
8621                 this.totalWidth += this.getColumnWidth(i);
8622             }
8623         }
8624         return totalWidth;
8625     },
8626
8627     getLockedCount : function(){
8628         for(var i = 0, len = this.config.length; i < len; i++){
8629             if(!this.isLocked(i)){
8630                 return i;
8631             }
8632         }
8633         
8634         return this.config.length;
8635     },
8636
8637     /**
8638      * Returns the number of columns.
8639      * @return {Number}
8640      */
8641     getColumnCount : function(visibleOnly){
8642         if(visibleOnly === true){
8643             var c = 0;
8644             for(var i = 0, len = this.config.length; i < len; i++){
8645                 if(!this.isHidden(i)){
8646                     c++;
8647                 }
8648             }
8649             return c;
8650         }
8651         return this.config.length;
8652     },
8653
8654     /**
8655      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8656      * @param {Function} fn
8657      * @param {Object} scope (optional)
8658      * @return {Array} result
8659      */
8660     getColumnsBy : function(fn, scope){
8661         var r = [];
8662         for(var i = 0, len = this.config.length; i < len; i++){
8663             var c = this.config[i];
8664             if(fn.call(scope||this, c, i) === true){
8665                 r[r.length] = c;
8666             }
8667         }
8668         return r;
8669     },
8670
8671     /**
8672      * Returns true if the specified column is sortable.
8673      * @param {Number} col The column index
8674      * @return {Boolean}
8675      */
8676     isSortable : function(col){
8677         if(typeof this.config[col].sortable == "undefined"){
8678             return this.defaultSortable;
8679         }
8680         return this.config[col].sortable;
8681     },
8682
8683     /**
8684      * Returns the rendering (formatting) function defined for the column.
8685      * @param {Number} col The column index.
8686      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8687      */
8688     getRenderer : function(col){
8689         if(!this.config[col].renderer){
8690             return Roo.grid.ColumnModel.defaultRenderer;
8691         }
8692         return this.config[col].renderer;
8693     },
8694
8695     /**
8696      * Sets the rendering (formatting) function for a column.
8697      * @param {Number} col The column index
8698      * @param {Function} fn The function to use to process the cell's raw data
8699      * to return HTML markup for the grid view. The render function is called with
8700      * the following parameters:<ul>
8701      * <li>Data value.</li>
8702      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8703      * <li>css A CSS style string to apply to the table cell.</li>
8704      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8705      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8706      * <li>Row index</li>
8707      * <li>Column index</li>
8708      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8709      */
8710     setRenderer : function(col, fn){
8711         this.config[col].renderer = fn;
8712     },
8713
8714     /**
8715      * Returns the width for the specified column.
8716      * @param {Number} col The column index
8717      * @param (optional) {String} gridSize bootstrap width size.
8718      * @return {Number}
8719      */
8720     getColumnWidth : function(col, gridSize)
8721         {
8722                 var cfg = this.config[col];
8723                 
8724                 if (typeof(gridSize) == 'undefined') {
8725                         return cfg.width * 1 || this.defaultWidth;
8726                 }
8727                 if (gridSize === false) { // if we set it..
8728                         return cfg.width || false;
8729                 }
8730                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8731                 
8732                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8733                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8734                                 continue;
8735                         }
8736                         return cfg[ sizes[i] ];
8737                 }
8738                 return 1;
8739                 
8740     },
8741
8742     /**
8743      * Sets the width for a column.
8744      * @param {Number} col The column index
8745      * @param {Number} width The new width
8746      */
8747     setColumnWidth : function(col, width, suppressEvent){
8748         this.config[col].width = width;
8749         this.totalWidth = null;
8750         if(!suppressEvent){
8751              this.fireEvent("widthchange", this, col, width);
8752         }
8753     },
8754
8755     /**
8756      * Returns the total width of all columns.
8757      * @param {Boolean} includeHidden True to include hidden column widths
8758      * @return {Number}
8759      */
8760     getTotalWidth : function(includeHidden){
8761         if(!this.totalWidth){
8762             this.totalWidth = 0;
8763             for(var i = 0, len = this.config.length; i < len; i++){
8764                 if(includeHidden || !this.isHidden(i)){
8765                     this.totalWidth += this.getColumnWidth(i);
8766                 }
8767             }
8768         }
8769         return this.totalWidth;
8770     },
8771
8772     /**
8773      * Returns the header for the specified column.
8774      * @param {Number} col The column index
8775      * @return {String}
8776      */
8777     getColumnHeader : function(col){
8778         return this.config[col].header;
8779     },
8780
8781     /**
8782      * Sets the header for a column.
8783      * @param {Number} col The column index
8784      * @param {String} header The new header
8785      */
8786     setColumnHeader : function(col, header){
8787         this.config[col].header = header;
8788         this.fireEvent("headerchange", this, col, header);
8789     },
8790
8791     /**
8792      * Returns the tooltip for the specified column.
8793      * @param {Number} col The column index
8794      * @return {String}
8795      */
8796     getColumnTooltip : function(col){
8797             return this.config[col].tooltip;
8798     },
8799     /**
8800      * Sets the tooltip for a column.
8801      * @param {Number} col The column index
8802      * @param {String} tooltip The new tooltip
8803      */
8804     setColumnTooltip : function(col, tooltip){
8805             this.config[col].tooltip = tooltip;
8806     },
8807
8808     /**
8809      * Returns the dataIndex for the specified column.
8810      * @param {Number} col The column index
8811      * @return {Number}
8812      */
8813     getDataIndex : function(col){
8814         return this.config[col].dataIndex;
8815     },
8816
8817     /**
8818      * Sets the dataIndex for a column.
8819      * @param {Number} col The column index
8820      * @param {Number} dataIndex The new dataIndex
8821      */
8822     setDataIndex : function(col, dataIndex){
8823         this.config[col].dataIndex = dataIndex;
8824     },
8825
8826     
8827     
8828     /**
8829      * Returns true if the cell is editable.
8830      * @param {Number} colIndex The column index
8831      * @param {Number} rowIndex The row index - this is nto actually used..?
8832      * @return {Boolean}
8833      */
8834     isCellEditable : function(colIndex, rowIndex){
8835         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8836     },
8837
8838     /**
8839      * Returns the editor defined for the cell/column.
8840      * return false or null to disable editing.
8841      * @param {Number} colIndex The column index
8842      * @param {Number} rowIndex The row index
8843      * @return {Object}
8844      */
8845     getCellEditor : function(colIndex, rowIndex){
8846         return this.config[colIndex].editor;
8847     },
8848
8849     /**
8850      * Sets if a column is editable.
8851      * @param {Number} col The column index
8852      * @param {Boolean} editable True if the column is editable
8853      */
8854     setEditable : function(col, editable){
8855         this.config[col].editable = editable;
8856     },
8857
8858
8859     /**
8860      * Returns true if the column is hidden.
8861      * @param {Number} colIndex The column index
8862      * @return {Boolean}
8863      */
8864     isHidden : function(colIndex){
8865         return this.config[colIndex].hidden;
8866     },
8867
8868
8869     /**
8870      * Returns true if the column width cannot be changed
8871      */
8872     isFixed : function(colIndex){
8873         return this.config[colIndex].fixed;
8874     },
8875
8876     /**
8877      * Returns true if the column can be resized
8878      * @return {Boolean}
8879      */
8880     isResizable : function(colIndex){
8881         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8882     },
8883     /**
8884      * Sets if a column is hidden.
8885      * @param {Number} colIndex The column index
8886      * @param {Boolean} hidden True if the column is hidden
8887      */
8888     setHidden : function(colIndex, hidden){
8889         this.config[colIndex].hidden = hidden;
8890         this.totalWidth = null;
8891         this.fireEvent("hiddenchange", this, colIndex, hidden);
8892     },
8893
8894     /**
8895      * Sets the editor for a column.
8896      * @param {Number} col The column index
8897      * @param {Object} editor The editor object
8898      */
8899     setEditor : function(col, editor){
8900         this.config[col].editor = editor;
8901     },
8902     /**
8903      * Add a column (experimental...) - defaults to adding to the end..
8904      * @param {Object} config 
8905     */
8906     addColumn : function(c)
8907     {
8908     
8909         var i = this.config.length;
8910         this.config[i] = c;
8911         
8912         if(typeof c.dataIndex == "undefined"){
8913             c.dataIndex = i;
8914         }
8915         if(typeof c.renderer == "string"){
8916             c.renderer = Roo.util.Format[c.renderer];
8917         }
8918         if(typeof c.id == "undefined"){
8919             c.id = Roo.id();
8920         }
8921         if(c.editor && c.editor.xtype){
8922             c.editor  = Roo.factory(c.editor, Roo.grid);
8923         }
8924         if(c.editor && c.editor.isFormField){
8925             c.editor = new Roo.grid.GridEditor(c.editor);
8926         }
8927         this.lookup[c.id] = c;
8928     }
8929     
8930 });
8931
8932 Roo.grid.ColumnModel.defaultRenderer = function(value)
8933 {
8934     if(typeof value == "object") {
8935         return value;
8936     }
8937         if(typeof value == "string" && value.length < 1){
8938             return "&#160;";
8939         }
8940     
8941         return String.format("{0}", value);
8942 };
8943
8944 // Alias for backwards compatibility
8945 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8946 /*
8947  * Based on:
8948  * Ext JS Library 1.1.1
8949  * Copyright(c) 2006-2007, Ext JS, LLC.
8950  *
8951  * Originally Released Under LGPL - original licence link has changed is not relivant.
8952  *
8953  * Fork - LGPL
8954  * <script type="text/javascript">
8955  */
8956  
8957 /**
8958  * @class Roo.LoadMask
8959  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8960  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8961  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8962  * element's UpdateManager load indicator and will be destroyed after the initial load.
8963  * @constructor
8964  * Create a new LoadMask
8965  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8966  * @param {Object} config The config object
8967  */
8968 Roo.LoadMask = function(el, config){
8969     this.el = Roo.get(el);
8970     Roo.apply(this, config);
8971     if(this.store){
8972         this.store.on('beforeload', this.onBeforeLoad, this);
8973         this.store.on('load', this.onLoad, this);
8974         this.store.on('loadexception', this.onLoadException, this);
8975         this.removeMask = false;
8976     }else{
8977         var um = this.el.getUpdateManager();
8978         um.showLoadIndicator = false; // disable the default indicator
8979         um.on('beforeupdate', this.onBeforeLoad, this);
8980         um.on('update', this.onLoad, this);
8981         um.on('failure', this.onLoad, this);
8982         this.removeMask = true;
8983     }
8984 };
8985
8986 Roo.LoadMask.prototype = {
8987     /**
8988      * @cfg {Boolean} removeMask
8989      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8990      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8991      */
8992     removeMask : false,
8993     /**
8994      * @cfg {String} msg
8995      * The text to display in a centered loading message box (defaults to 'Loading...')
8996      */
8997     msg : 'Loading...',
8998     /**
8999      * @cfg {String} msgCls
9000      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9001      */
9002     msgCls : 'x-mask-loading',
9003
9004     /**
9005      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9006      * @type Boolean
9007      */
9008     disabled: false,
9009
9010     /**
9011      * Disables the mask to prevent it from being displayed
9012      */
9013     disable : function(){
9014        this.disabled = true;
9015     },
9016
9017     /**
9018      * Enables the mask so that it can be displayed
9019      */
9020     enable : function(){
9021         this.disabled = false;
9022     },
9023     
9024     onLoadException : function()
9025     {
9026         Roo.log(arguments);
9027         
9028         if (typeof(arguments[3]) != 'undefined') {
9029             Roo.MessageBox.alert("Error loading",arguments[3]);
9030         } 
9031         /*
9032         try {
9033             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9034                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9035             }   
9036         } catch(e) {
9037             
9038         }
9039         */
9040     
9041         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9042     },
9043     // private
9044     onLoad : function()
9045     {
9046         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9047     },
9048
9049     // private
9050     onBeforeLoad : function(){
9051         if(!this.disabled){
9052             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9053         }
9054     },
9055
9056     // private
9057     destroy : function(){
9058         if(this.store){
9059             this.store.un('beforeload', this.onBeforeLoad, this);
9060             this.store.un('load', this.onLoad, this);
9061             this.store.un('loadexception', this.onLoadException, this);
9062         }else{
9063             var um = this.el.getUpdateManager();
9064             um.un('beforeupdate', this.onBeforeLoad, this);
9065             um.un('update', this.onLoad, this);
9066             um.un('failure', this.onLoad, this);
9067         }
9068     }
9069 };/**
9070  * @class Roo.bootstrap.Table
9071  * @licence LGBL
9072  * @extends Roo.bootstrap.Component
9073  * @children Roo.bootstrap.TableBody
9074  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9075  * Similar to Roo.grid.Grid
9076  * <pre><code>
9077  var table = Roo.factory({
9078     xtype : 'Table',
9079     xns : Roo.bootstrap,
9080     autoSizeColumns: true,
9081     
9082     
9083     store : {
9084         xtype : 'Store',
9085         xns : Roo.data,
9086         remoteSort : true,
9087         sortInfo : { direction : 'ASC', field: 'name' },
9088         proxy : {
9089            xtype : 'HttpProxy',
9090            xns : Roo.data,
9091            method : 'GET',
9092            url : 'https://example.com/some.data.url.json'
9093         },
9094         reader : {
9095            xtype : 'JsonReader',
9096            xns : Roo.data,
9097            fields : [ 'id', 'name', whatever' ],
9098            id : 'id',
9099            root : 'data'
9100         }
9101     },
9102     cm : [
9103         {
9104             xtype : 'ColumnModel',
9105             xns : Roo.grid,
9106             align : 'center',
9107             cursor : 'pointer',
9108             dataIndex : 'is_in_group',
9109             header : "Name",
9110             sortable : true,
9111             renderer : function(v, x , r) {  
9112             
9113                 return String.format("{0}", v)
9114             }
9115             width : 3
9116         } // more columns..
9117     ],
9118     selModel : {
9119         xtype : 'RowSelectionModel',
9120         xns : Roo.bootstrap.Table
9121         // you can add listeners to catch selection change here....
9122     }
9123      
9124
9125  });
9126  // set any options
9127  grid.render(Roo.get("some-div"));
9128 </code></pre>
9129
9130 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9131
9132
9133
9134  *
9135  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9136  * @cfg {Roo.data.Store} store The data store to use
9137  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9138  * 
9139  * @cfg {String} cls table class
9140  *
9141  * 
9142  * @cfg {boolean} striped Should the rows be alternative striped
9143  * @cfg {boolean} bordered Add borders to the table
9144  * @cfg {boolean} hover Add hover highlighting
9145  * @cfg {boolean} condensed Format condensed
9146  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9147  *                also adds table-responsive (see bootstrap docs for details)
9148  * @cfg {Boolean} loadMask (true|false) default false
9149  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9150  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9151  * @cfg {Boolean} rowSelection (true|false) default false
9152  * @cfg {Boolean} cellSelection (true|false) default false
9153  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9154  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9155  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9156  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9157  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9158  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9159  * 
9160  * @constructor
9161  * Create a new Table
9162  * @param {Object} config The config object
9163  */
9164
9165 Roo.bootstrap.Table = function(config)
9166 {
9167     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9168      
9169     // BC...
9170     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9171     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9172     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9173     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9174     
9175     this.view = this; // compat with grid.
9176     
9177     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9178     if (this.sm) {
9179         this.sm.grid = this;
9180         this.selModel = Roo.factory(this.sm, Roo.grid);
9181         this.sm = this.selModel;
9182         this.sm.xmodule = this.xmodule || false;
9183     }
9184     
9185     if (this.cm && typeof(this.cm.config) == 'undefined') {
9186         this.colModel = new Roo.grid.ColumnModel(this.cm);
9187         this.cm = this.colModel;
9188         this.cm.xmodule = this.xmodule || false;
9189     }
9190     if (this.store) {
9191         this.store= Roo.factory(this.store, Roo.data);
9192         this.ds = this.store;
9193         this.ds.xmodule = this.xmodule || false;
9194          
9195     }
9196     if (this.footer && this.store) {
9197         this.footer.dataSource = this.ds;
9198         this.footer = Roo.factory(this.footer);
9199     }
9200     
9201     /** @private */
9202     this.addEvents({
9203         /**
9204          * @event cellclick
9205          * Fires when a cell is clicked
9206          * @param {Roo.bootstrap.Table} this
9207          * @param {Roo.Element} el
9208          * @param {Number} rowIndex
9209          * @param {Number} columnIndex
9210          * @param {Roo.EventObject} e
9211          */
9212         "cellclick" : true,
9213         /**
9214          * @event celldblclick
9215          * Fires when a cell is double clicked
9216          * @param {Roo.bootstrap.Table} this
9217          * @param {Roo.Element} el
9218          * @param {Number} rowIndex
9219          * @param {Number} columnIndex
9220          * @param {Roo.EventObject} e
9221          */
9222         "celldblclick" : true,
9223         /**
9224          * @event rowclick
9225          * Fires when a row is clicked
9226          * @param {Roo.bootstrap.Table} this
9227          * @param {Roo.Element} el
9228          * @param {Number} rowIndex
9229          * @param {Roo.EventObject} e
9230          */
9231         "rowclick" : true,
9232         /**
9233          * @event rowdblclick
9234          * Fires when a row is double clicked
9235          * @param {Roo.bootstrap.Table} this
9236          * @param {Roo.Element} el
9237          * @param {Number} rowIndex
9238          * @param {Roo.EventObject} e
9239          */
9240         "rowdblclick" : true,
9241         /**
9242          * @event mouseover
9243          * Fires when a mouseover occur
9244          * @param {Roo.bootstrap.Table} this
9245          * @param {Roo.Element} el
9246          * @param {Number} rowIndex
9247          * @param {Number} columnIndex
9248          * @param {Roo.EventObject} e
9249          */
9250         "mouseover" : true,
9251         /**
9252          * @event mouseout
9253          * Fires when a mouseout occur
9254          * @param {Roo.bootstrap.Table} this
9255          * @param {Roo.Element} el
9256          * @param {Number} rowIndex
9257          * @param {Number} columnIndex
9258          * @param {Roo.EventObject} e
9259          */
9260         "mouseout" : true,
9261         /**
9262          * @event rowclass
9263          * Fires when a row is rendered, so you can change add a style to it.
9264          * @param {Roo.bootstrap.Table} this
9265          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9266          */
9267         'rowclass' : true,
9268           /**
9269          * @event rowsrendered
9270          * Fires when all the  rows have been rendered
9271          * @param {Roo.bootstrap.Table} this
9272          */
9273         'rowsrendered' : true,
9274         /**
9275          * @event contextmenu
9276          * The raw contextmenu event for the entire grid.
9277          * @param {Roo.EventObject} e
9278          */
9279         "contextmenu" : true,
9280         /**
9281          * @event rowcontextmenu
9282          * Fires when a row is right clicked
9283          * @param {Roo.bootstrap.Table} this
9284          * @param {Number} rowIndex
9285          * @param {Roo.EventObject} e
9286          */
9287         "rowcontextmenu" : true,
9288         /**
9289          * @event cellcontextmenu
9290          * Fires when a cell is right clicked
9291          * @param {Roo.bootstrap.Table} this
9292          * @param {Number} rowIndex
9293          * @param {Number} cellIndex
9294          * @param {Roo.EventObject} e
9295          */
9296          "cellcontextmenu" : true,
9297          /**
9298          * @event headercontextmenu
9299          * Fires when a header is right clicked
9300          * @param {Roo.bootstrap.Table} this
9301          * @param {Number} columnIndex
9302          * @param {Roo.EventObject} e
9303          */
9304         "headercontextmenu" : true,
9305         /**
9306          * @event mousedown
9307          * The raw mousedown event for the entire grid.
9308          * @param {Roo.EventObject} e
9309          */
9310         "mousedown" : true
9311         
9312     });
9313 };
9314
9315 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9316     
9317     cls: false,
9318     
9319     striped : false,
9320     scrollBody : false,
9321     bordered: false,
9322     hover:  false,
9323     condensed : false,
9324     responsive : false,
9325     sm : false,
9326     cm : false,
9327     store : false,
9328     loadMask : false,
9329     footerShow : true,
9330     headerShow : true,
9331     enableColumnResize: true,
9332   
9333     rowSelection : false,
9334     cellSelection : false,
9335     layout : false,
9336
9337     minColumnWidth : 50,
9338     
9339     // Roo.Element - the tbody
9340     bodyEl: false,  // <tbody> Roo.Element - thead element    
9341     headEl: false,  // <thead> Roo.Element - thead element
9342     resizeProxy : false, // proxy element for dragging?
9343
9344
9345     
9346     container: false, // used by gridpanel...
9347     
9348     lazyLoad : false,
9349     
9350     CSS : Roo.util.CSS,
9351     
9352     auto_hide_footer : false,
9353     
9354     view: false, // actually points to this..
9355     
9356     getAutoCreate : function()
9357     {
9358         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9359         
9360         cfg = {
9361             tag: 'table',
9362             cls : 'table', 
9363             cn : []
9364         };
9365         // this get's auto added by panel.Grid
9366         if (this.scrollBody) {
9367             cfg.cls += ' table-body-fixed';
9368         }    
9369         if (this.striped) {
9370             cfg.cls += ' table-striped';
9371         }
9372         
9373         if (this.hover) {
9374             cfg.cls += ' table-hover';
9375         }
9376         if (this.bordered) {
9377             cfg.cls += ' table-bordered';
9378         }
9379         if (this.condensed) {
9380             cfg.cls += ' table-condensed';
9381         }
9382         
9383         if (this.responsive) {
9384             cfg.cls += ' table-responsive';
9385         }
9386         
9387         if (this.cls) {
9388             cfg.cls+=  ' ' +this.cls;
9389         }
9390         
9391         
9392         
9393         if (this.layout) {
9394             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9395         }
9396         
9397         if(this.store || this.cm){
9398             if(this.headerShow){
9399                 cfg.cn.push(this.renderHeader());
9400             }
9401             
9402             cfg.cn.push(this.renderBody());
9403             
9404             if(this.footerShow){
9405                 cfg.cn.push(this.renderFooter());
9406             }
9407             // where does this come from?
9408             //cfg.cls+=  ' TableGrid';
9409         }
9410         
9411         return { cn : [ cfg ] };
9412     },
9413     
9414     initEvents : function()
9415     {   
9416         if(!this.store || !this.cm){
9417             return;
9418         }
9419         if (this.selModel) {
9420             this.selModel.initEvents();
9421         }
9422         
9423         
9424         //Roo.log('initEvents with ds!!!!');
9425         
9426         this.bodyEl = this.el.select('tbody', true).first();
9427         this.headEl = this.el.select('thead', true).first();
9428         this.mainFoot = this.el.select('tfoot', true).first();
9429         
9430         
9431         
9432         
9433         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9434             e.on('click', this.sort, this);
9435         }, this);
9436         
9437         
9438         // why is this done????? = it breaks dialogs??
9439         //this.parent().el.setStyle('position', 'relative');
9440         
9441         
9442         if (this.footer) {
9443             this.footer.parentId = this.id;
9444             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9445             
9446             if(this.lazyLoad){
9447                 this.el.select('tfoot tr td').first().addClass('hide');
9448             }
9449         } 
9450         
9451         if(this.loadMask) {
9452             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9453         }
9454         
9455         this.store.on('load', this.onLoad, this);
9456         this.store.on('beforeload', this.onBeforeLoad, this);
9457         this.store.on('update', this.onUpdate, this);
9458         this.store.on('add', this.onAdd, this);
9459         this.store.on("clear", this.clear, this);
9460         
9461         this.el.on("contextmenu", this.onContextMenu, this);
9462         
9463         
9464         this.cm.on("headerchange", this.onHeaderChange, this);
9465         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9466
9467  //?? does bodyEl get replaced on render?
9468         this.bodyEl.on("click", this.onClick, this);
9469         this.bodyEl.on("dblclick", this.onDblClick, this);        
9470         this.bodyEl.on('scroll', this.onBodyScroll, this);
9471
9472         // guessing mainbody will work - this relays usually caught by selmodel at present.
9473         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9474   
9475   
9476         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9477         
9478   
9479         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9480             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9481         }
9482         
9483         this.initCSS();
9484     },
9485     // Compatibility with grid - we implement all the view features at present.
9486     getView : function()
9487     {
9488         return this;
9489     },
9490     
9491     initCSS : function()
9492     {
9493         
9494         
9495         var cm = this.cm, styles = [];
9496         this.CSS.removeStyleSheet(this.id + '-cssrules');
9497         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9498         // we can honour xs/sm/md/xl  as widths...
9499         // we first have to decide what widht we are currently at...
9500         var sz = Roo.getGridSize();
9501         
9502         var total = 0;
9503         var last = -1;
9504         var cols = []; // visable cols.
9505         var total_abs = 0;
9506         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9507             var w = cm.getColumnWidth(i, false);
9508             if(cm.isHidden(i)){
9509                 cols.push( { rel : false, abs : 0 });
9510                 continue;
9511             }
9512             if (w !== false) {
9513                 cols.push( { rel : false, abs : w });
9514                 total_abs += w;
9515                 last = i; // not really..
9516                 continue;
9517             }
9518             var w = cm.getColumnWidth(i, sz);
9519             if (w > 0) {
9520                 last = i
9521             }
9522             total += w;
9523             cols.push( { rel : w, abs : false });
9524         }
9525         
9526         var avail = this.bodyEl.dom.clientWidth - total_abs;
9527         
9528         var unitWidth = Math.floor(avail / total);
9529         var rem = avail - (unitWidth * total);
9530         
9531         var hidden, width, pos = 0 , splithide , left;
9532         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9533             
9534             hidden = 'display:none;';
9535             left = '';
9536             width  = 'width:0px;';
9537             splithide = '';
9538             if(!cm.isHidden(i)){
9539                 hidden = '';
9540                 
9541                 
9542                 // we can honour xs/sm/md/xl ?
9543                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9544                 if (w===0) {
9545                     hidden = 'display:none;';
9546                 }
9547                 // width should return a small number...
9548                 if (i == last) {
9549                     w+=rem; // add the remaining with..
9550                 }
9551                 pos += w;
9552                 left = "left:" + (pos -4) + "px;";
9553                 width = "width:" + w+ "px;";
9554                 
9555             }
9556             if (this.responsive) {
9557                 width = '';
9558                 left = '';
9559                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9560                 splithide = 'display: none;';
9561             }
9562             
9563             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9564             if (this.headEl) {
9565                 if (i == last) {
9566                     splithide = 'display:none;';
9567                 }
9568                 
9569                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9570                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9571                 );
9572             }
9573             
9574         }
9575         //Roo.log(styles.join(''));
9576         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9577         
9578     },
9579     
9580     
9581     
9582     onContextMenu : function(e, t)
9583     {
9584         this.processEvent("contextmenu", e);
9585     },
9586     
9587     processEvent : function(name, e)
9588     {
9589         if (name != 'touchstart' ) {
9590             this.fireEvent(name, e);    
9591         }
9592         
9593         var t = e.getTarget();
9594         
9595         var cell = Roo.get(t);
9596         
9597         if(!cell){
9598             return;
9599         }
9600         
9601         if(cell.findParent('tfoot', false, true)){
9602             return;
9603         }
9604         
9605         if(cell.findParent('thead', false, true)){
9606             
9607             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9608                 cell = Roo.get(t).findParent('th', false, true);
9609                 if (!cell) {
9610                     Roo.log("failed to find th in thead?");
9611                     Roo.log(e.getTarget());
9612                     return;
9613                 }
9614             }
9615             
9616             var cellIndex = cell.dom.cellIndex;
9617             
9618             var ename = name == 'touchstart' ? 'click' : name;
9619             this.fireEvent("header" + ename, this, cellIndex, e);
9620             
9621             return;
9622         }
9623         
9624         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9625             cell = Roo.get(t).findParent('td', false, true);
9626             if (!cell) {
9627                 Roo.log("failed to find th in tbody?");
9628                 Roo.log(e.getTarget());
9629                 return;
9630             }
9631         }
9632         
9633         var row = cell.findParent('tr', false, true);
9634         var cellIndex = cell.dom.cellIndex;
9635         var rowIndex = row.dom.rowIndex - 1;
9636         
9637         if(row !== false){
9638             
9639             this.fireEvent("row" + name, this, rowIndex, e);
9640             
9641             if(cell !== false){
9642             
9643                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9644             }
9645         }
9646         
9647     },
9648     
9649     onMouseover : function(e, el)
9650     {
9651         var cell = Roo.get(el);
9652         
9653         if(!cell){
9654             return;
9655         }
9656         
9657         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9658             cell = cell.findParent('td', false, true);
9659         }
9660         
9661         var row = cell.findParent('tr', false, true);
9662         var cellIndex = cell.dom.cellIndex;
9663         var rowIndex = row.dom.rowIndex - 1; // start from 0
9664         
9665         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9666         
9667     },
9668     
9669     onMouseout : function(e, el)
9670     {
9671         var cell = Roo.get(el);
9672         
9673         if(!cell){
9674             return;
9675         }
9676         
9677         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9678             cell = cell.findParent('td', false, true);
9679         }
9680         
9681         var row = cell.findParent('tr', false, true);
9682         var cellIndex = cell.dom.cellIndex;
9683         var rowIndex = row.dom.rowIndex - 1; // start from 0
9684         
9685         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9686         
9687     },
9688     
9689     onClick : function(e, el)
9690     {
9691         var cell = Roo.get(el);
9692         
9693         if(!cell || (!this.cellSelection && !this.rowSelection)){
9694             return;
9695         }
9696         
9697         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9698             cell = cell.findParent('td', false, true);
9699         }
9700         
9701         if(!cell || typeof(cell) == 'undefined'){
9702             return;
9703         }
9704         
9705         var row = cell.findParent('tr', false, true);
9706         
9707         if(!row || typeof(row) == 'undefined'){
9708             return;
9709         }
9710         
9711         var cellIndex = cell.dom.cellIndex;
9712         var rowIndex = this.getRowIndex(row);
9713         
9714         // why??? - should these not be based on SelectionModel?
9715         //if(this.cellSelection){
9716             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9717         //}
9718         
9719         //if(this.rowSelection){
9720             this.fireEvent('rowclick', this, row, rowIndex, e);
9721         //}
9722          
9723     },
9724         
9725     onDblClick : function(e,el)
9726     {
9727         var cell = Roo.get(el);
9728         
9729         if(!cell || (!this.cellSelection && !this.rowSelection)){
9730             return;
9731         }
9732         
9733         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9734             cell = cell.findParent('td', false, true);
9735         }
9736         
9737         if(!cell || typeof(cell) == 'undefined'){
9738             return;
9739         }
9740         
9741         var row = cell.findParent('tr', false, true);
9742         
9743         if(!row || typeof(row) == 'undefined'){
9744             return;
9745         }
9746         
9747         var cellIndex = cell.dom.cellIndex;
9748         var rowIndex = this.getRowIndex(row);
9749         
9750         if(this.cellSelection){
9751             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9752         }
9753         
9754         if(this.rowSelection){
9755             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9756         }
9757     },
9758     findRowIndex : function(el)
9759     {
9760         var cell = Roo.get(el);
9761         if(!cell) {
9762             return false;
9763         }
9764         var row = cell.findParent('tr', false, true);
9765         
9766         if(!row || typeof(row) == 'undefined'){
9767             return false;
9768         }
9769         return this.getRowIndex(row);
9770     },
9771     sort : function(e,el)
9772     {
9773         var col = Roo.get(el);
9774         
9775         if(!col.hasClass('sortable')){
9776             return;
9777         }
9778         
9779         var sort = col.attr('sort');
9780         var dir = 'ASC';
9781         
9782         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9783             dir = 'DESC';
9784         }
9785         
9786         this.store.sortInfo = {field : sort, direction : dir};
9787         
9788         if (this.footer) {
9789             Roo.log("calling footer first");
9790             this.footer.onClick('first');
9791         } else {
9792         
9793             this.store.load({ params : { start : 0 } });
9794         }
9795     },
9796     
9797     renderHeader : function()
9798     {
9799         var header = {
9800             tag: 'thead',
9801             cn : []
9802         };
9803         
9804         var cm = this.cm;
9805         this.totalWidth = 0;
9806         
9807         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9808             
9809             var config = cm.config[i];
9810             
9811             var c = {
9812                 tag: 'th',
9813                 cls : 'x-hcol-' + i,
9814                 style : '',
9815                 
9816                 html: cm.getColumnHeader(i)
9817             };
9818             
9819             var tooltip = cm.getColumnTooltip(i);
9820             if (tooltip) {
9821                 c.tooltip = tooltip;
9822             }
9823             
9824             
9825             var hh = '';
9826             
9827             if(typeof(config.sortable) != 'undefined' && config.sortable){
9828                 c.cls += ' sortable';
9829                 c.html = '<i class="fa"></i>' + c.html;
9830             }
9831             
9832             // could use BS4 hidden-..-down 
9833             
9834             if(typeof(config.lgHeader) != 'undefined'){
9835                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9836             }
9837             
9838             if(typeof(config.mdHeader) != 'undefined'){
9839                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9840             }
9841             
9842             if(typeof(config.smHeader) != 'undefined'){
9843                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9844             }
9845             
9846             if(typeof(config.xsHeader) != 'undefined'){
9847                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9848             }
9849             
9850             if(hh.length){
9851                 c.html = hh;
9852             }
9853             
9854             if(typeof(config.tooltip) != 'undefined'){
9855                 c.tooltip = config.tooltip;
9856             }
9857             
9858             if(typeof(config.colspan) != 'undefined'){
9859                 c.colspan = config.colspan;
9860             }
9861             
9862             // hidden is handled by CSS now
9863             
9864             if(typeof(config.dataIndex) != 'undefined'){
9865                 c.sort = config.dataIndex;
9866             }
9867             
9868            
9869             
9870             if(typeof(config.align) != 'undefined' && config.align.length){
9871                 c.style += ' text-align:' + config.align + ';';
9872             }
9873             
9874             /* width is done in CSS
9875              *if(typeof(config.width) != 'undefined'){
9876                 c.style += ' width:' + config.width + 'px;';
9877                 this.totalWidth += config.width;
9878             } else {
9879                 this.totalWidth += 100; // assume minimum of 100 per column?
9880             }
9881             */
9882             
9883             if(typeof(config.cls) != 'undefined'){
9884                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9885             }
9886             // this is the bit that doesnt reall work at all...
9887             
9888             if (this.responsive) {
9889                  
9890             
9891                 ['xs','sm','md','lg'].map(function(size){
9892                     
9893                     if(typeof(config[size]) == 'undefined'){
9894                         return;
9895                     }
9896                      
9897                     if (!config[size]) { // 0 = hidden
9898                         // BS 4 '0' is treated as hide that column and below.
9899                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9900                         return;
9901                     }
9902                     
9903                     c.cls += ' col-' + size + '-' + config[size] + (
9904                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9905                     );
9906                     
9907                     
9908                 });
9909             }
9910             // at the end?
9911             
9912             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9913             
9914             
9915             
9916             
9917             header.cn.push(c)
9918         }
9919         
9920         return header;
9921     },
9922     
9923     renderBody : function()
9924     {
9925         var body = {
9926             tag: 'tbody',
9927             cn : [
9928                 {
9929                     tag: 'tr',
9930                     cn : [
9931                         {
9932                             tag : 'td',
9933                             colspan :  this.cm.getColumnCount()
9934                         }
9935                     ]
9936                 }
9937             ]
9938         };
9939         
9940         return body;
9941     },
9942     
9943     renderFooter : function()
9944     {
9945         var footer = {
9946             tag: 'tfoot',
9947             cn : [
9948                 {
9949                     tag: 'tr',
9950                     cn : [
9951                         {
9952                             tag : 'td',
9953                             colspan :  this.cm.getColumnCount()
9954                         }
9955                     ]
9956                 }
9957             ]
9958         };
9959         
9960         return footer;
9961     },
9962     
9963     
9964     
9965     onLoad : function()
9966     {
9967 //        Roo.log('ds onload');
9968         this.clear();
9969         
9970         var _this = this;
9971         var cm = this.cm;
9972         var ds = this.store;
9973         
9974         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9975             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9976             if (_this.store.sortInfo) {
9977                     
9978                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9979                     e.select('i', true).addClass(['fa-arrow-up']);
9980                 }
9981                 
9982                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9983                     e.select('i', true).addClass(['fa-arrow-down']);
9984                 }
9985             }
9986         });
9987         
9988         var tbody =  this.bodyEl;
9989               
9990         if(ds.getCount() > 0){
9991             ds.data.each(function(d,rowIndex){
9992                 var row =  this.renderRow(cm, ds, rowIndex);
9993                 
9994                 tbody.createChild(row);
9995                 
9996                 var _this = this;
9997                 
9998                 if(row.cellObjects.length){
9999                     Roo.each(row.cellObjects, function(r){
10000                         _this.renderCellObject(r);
10001                     })
10002                 }
10003                 
10004             }, this);
10005         }
10006         
10007         var tfoot = this.el.select('tfoot', true).first();
10008         
10009         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10010             
10011             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10012             
10013             var total = this.ds.getTotalCount();
10014             
10015             if(this.footer.pageSize < total){
10016                 this.mainFoot.show();
10017             }
10018         }
10019         
10020         Roo.each(this.el.select('tbody td', true).elements, function(e){
10021             e.on('mouseover', _this.onMouseover, _this);
10022         });
10023         
10024         Roo.each(this.el.select('tbody td', true).elements, function(e){
10025             e.on('mouseout', _this.onMouseout, _this);
10026         });
10027         this.fireEvent('rowsrendered', this);
10028         
10029         this.autoSize();
10030         
10031         this.initCSS(); /// resize cols
10032
10033         
10034     },
10035     
10036     
10037     onUpdate : function(ds,record)
10038     {
10039         this.refreshRow(record);
10040         this.autoSize();
10041     },
10042     
10043     onRemove : function(ds, record, index, isUpdate){
10044         if(isUpdate !== true){
10045             this.fireEvent("beforerowremoved", this, index, record);
10046         }
10047         var bt = this.bodyEl.dom;
10048         
10049         var rows = this.el.select('tbody > tr', true).elements;
10050         
10051         if(typeof(rows[index]) != 'undefined'){
10052             bt.removeChild(rows[index].dom);
10053         }
10054         
10055 //        if(bt.rows[index]){
10056 //            bt.removeChild(bt.rows[index]);
10057 //        }
10058         
10059         if(isUpdate !== true){
10060             //this.stripeRows(index);
10061             //this.syncRowHeights(index, index);
10062             //this.layout();
10063             this.fireEvent("rowremoved", this, index, record);
10064         }
10065     },
10066     
10067     onAdd : function(ds, records, rowIndex)
10068     {
10069         //Roo.log('on Add called');
10070         // - note this does not handle multiple adding very well..
10071         var bt = this.bodyEl.dom;
10072         for (var i =0 ; i < records.length;i++) {
10073             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10074             //Roo.log(records[i]);
10075             //Roo.log(this.store.getAt(rowIndex+i));
10076             this.insertRow(this.store, rowIndex + i, false);
10077             return;
10078         }
10079         
10080     },
10081     
10082     
10083     refreshRow : function(record){
10084         var ds = this.store, index;
10085         if(typeof record == 'number'){
10086             index = record;
10087             record = ds.getAt(index);
10088         }else{
10089             index = ds.indexOf(record);
10090             if (index < 0) {
10091                 return; // should not happen - but seems to 
10092             }
10093         }
10094         this.insertRow(ds, index, true);
10095         this.autoSize();
10096         this.onRemove(ds, record, index+1, true);
10097         this.autoSize();
10098         //this.syncRowHeights(index, index);
10099         //this.layout();
10100         this.fireEvent("rowupdated", this, index, record);
10101     },
10102     // private - called by RowSelection
10103     onRowSelect : function(rowIndex){
10104         var row = this.getRowDom(rowIndex);
10105         row.addClass(['bg-info','info']);
10106     },
10107     // private - called by RowSelection
10108     onRowDeselect : function(rowIndex)
10109     {
10110         if (rowIndex < 0) {
10111             return;
10112         }
10113         var row = this.getRowDom(rowIndex);
10114         row.removeClass(['bg-info','info']);
10115     },
10116       /**
10117      * Focuses the specified row.
10118      * @param {Number} row The row index
10119      */
10120     focusRow : function(row)
10121     {
10122         //Roo.log('GridView.focusRow');
10123         var x = this.bodyEl.dom.scrollLeft;
10124         this.focusCell(row, 0, false);
10125         this.bodyEl.dom.scrollLeft = x;
10126
10127     },
10128      /**
10129      * Focuses the specified cell.
10130      * @param {Number} row The row index
10131      * @param {Number} col The column index
10132      * @param {Boolean} hscroll false to disable horizontal scrolling
10133      */
10134     focusCell : function(row, col, hscroll)
10135     {
10136         //Roo.log('GridView.focusCell');
10137         var el = this.ensureVisible(row, col, hscroll);
10138         // not sure what focusEL achives = it's a <a> pos relative 
10139         //this.focusEl.alignTo(el, "tl-tl");
10140         //if(Roo.isGecko){
10141         //    this.focusEl.focus();
10142         //}else{
10143         //    this.focusEl.focus.defer(1, this.focusEl);
10144         //}
10145     },
10146     
10147      /**
10148      * Scrolls the specified cell into view
10149      * @param {Number} row The row index
10150      * @param {Number} col The column index
10151      * @param {Boolean} hscroll false to disable horizontal scrolling
10152      */
10153     ensureVisible : function(row, col, hscroll)
10154     {
10155         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10156         //return null; //disable for testing.
10157         if(typeof row != "number"){
10158             row = row.rowIndex;
10159         }
10160         if(row < 0 && row >= this.ds.getCount()){
10161             return  null;
10162         }
10163         col = (col !== undefined ? col : 0);
10164         var cm = this.cm;
10165         while(cm.isHidden(col)){
10166             col++;
10167         }
10168
10169         var el = this.getCellDom(row, col);
10170         if(!el){
10171             return null;
10172         }
10173         var c = this.bodyEl.dom;
10174
10175         var ctop = parseInt(el.offsetTop, 10);
10176         var cleft = parseInt(el.offsetLeft, 10);
10177         var cbot = ctop + el.offsetHeight;
10178         var cright = cleft + el.offsetWidth;
10179
10180         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10181         var ch = 0; //?? header is not withing the area?
10182         var stop = parseInt(c.scrollTop, 10);
10183         var sleft = parseInt(c.scrollLeft, 10);
10184         var sbot = stop + ch;
10185         var sright = sleft + c.clientWidth;
10186         /*
10187         Roo.log('GridView.ensureVisible:' +
10188                 ' ctop:' + ctop +
10189                 ' c.clientHeight:' + c.clientHeight +
10190                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10191                 ' stop:' + stop +
10192                 ' cbot:' + cbot +
10193                 ' sbot:' + sbot +
10194                 ' ch:' + ch  
10195                 );
10196         */
10197         if(ctop < stop){
10198             c.scrollTop = ctop;
10199             //Roo.log("set scrolltop to ctop DISABLE?");
10200         }else if(cbot > sbot){
10201             //Roo.log("set scrolltop to cbot-ch");
10202             c.scrollTop = cbot-ch;
10203         }
10204
10205         if(hscroll !== false){
10206             if(cleft < sleft){
10207                 c.scrollLeft = cleft;
10208             }else if(cright > sright){
10209                 c.scrollLeft = cright-c.clientWidth;
10210             }
10211         }
10212
10213         return el;
10214     },
10215     
10216     
10217     insertRow : function(dm, rowIndex, isUpdate){
10218         
10219         if(!isUpdate){
10220             this.fireEvent("beforerowsinserted", this, rowIndex);
10221         }
10222             //var s = this.getScrollState();
10223         var row = this.renderRow(this.cm, this.store, rowIndex);
10224         // insert before rowIndex..
10225         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10226         
10227         var _this = this;
10228                 
10229         if(row.cellObjects.length){
10230             Roo.each(row.cellObjects, function(r){
10231                 _this.renderCellObject(r);
10232             })
10233         }
10234             
10235         if(!isUpdate){
10236             this.fireEvent("rowsinserted", this, rowIndex);
10237             //this.syncRowHeights(firstRow, lastRow);
10238             //this.stripeRows(firstRow);
10239             //this.layout();
10240         }
10241         
10242     },
10243     
10244     
10245     getRowDom : function(rowIndex)
10246     {
10247         var rows = this.el.select('tbody > tr', true).elements;
10248         
10249         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10250         
10251     },
10252     getCellDom : function(rowIndex, colIndex)
10253     {
10254         var row = this.getRowDom(rowIndex);
10255         if (row === false) {
10256             return false;
10257         }
10258         var cols = row.select('td', true).elements;
10259         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10260         
10261     },
10262     
10263     // returns the object tree for a tr..
10264   
10265     
10266     renderRow : function(cm, ds, rowIndex) 
10267     {
10268         var d = ds.getAt(rowIndex);
10269         
10270         var row = {
10271             tag : 'tr',
10272             cls : 'x-row-' + rowIndex,
10273             cn : []
10274         };
10275             
10276         var cellObjects = [];
10277         
10278         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10279             var config = cm.config[i];
10280             
10281             var renderer = cm.getRenderer(i);
10282             var value = '';
10283             var id = false;
10284             
10285             if(typeof(renderer) !== 'undefined'){
10286                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10287             }
10288             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10289             // and are rendered into the cells after the row is rendered - using the id for the element.
10290             
10291             if(typeof(value) === 'object'){
10292                 id = Roo.id();
10293                 cellObjects.push({
10294                     container : id,
10295                     cfg : value 
10296                 })
10297             }
10298             
10299             var rowcfg = {
10300                 record: d,
10301                 rowIndex : rowIndex,
10302                 colIndex : i,
10303                 rowClass : ''
10304             };
10305
10306             this.fireEvent('rowclass', this, rowcfg);
10307             
10308             var td = {
10309                 tag: 'td',
10310                 // this might end up displaying HTML?
10311                 // this is too messy... - better to only do it on columsn you know are going to be too long
10312                 //tooltip : (typeof(value) === 'object') ? '' : value,
10313                 cls : rowcfg.rowClass + ' x-col-' + i,
10314                 style: '',
10315                 html: (typeof(value) === 'object') ? '' : value
10316             };
10317             
10318             if (id) {
10319                 td.id = id;
10320             }
10321             
10322             if(typeof(config.colspan) != 'undefined'){
10323                 td.colspan = config.colspan;
10324             }
10325             
10326             
10327             
10328             if(typeof(config.align) != 'undefined' && config.align.length){
10329                 td.style += ' text-align:' + config.align + ';';
10330             }
10331             if(typeof(config.valign) != 'undefined' && config.valign.length){
10332                 td.style += ' vertical-align:' + config.valign + ';';
10333             }
10334             /*
10335             if(typeof(config.width) != 'undefined'){
10336                 td.style += ' width:' +  config.width + 'px;';
10337             }
10338             */
10339             
10340             if(typeof(config.cursor) != 'undefined'){
10341                 td.style += ' cursor:' +  config.cursor + ';';
10342             }
10343             
10344             if(typeof(config.cls) != 'undefined'){
10345                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10346             }
10347             if (this.responsive) {
10348                 ['xs','sm','md','lg'].map(function(size){
10349                     
10350                     if(typeof(config[size]) == 'undefined'){
10351                         return;
10352                     }
10353                     
10354                     
10355                       
10356                     if (!config[size]) { // 0 = hidden
10357                         // BS 4 '0' is treated as hide that column and below.
10358                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10359                         return;
10360                     }
10361                     
10362                     td.cls += ' col-' + size + '-' + config[size] + (
10363                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10364                     );
10365                      
10366     
10367                 });
10368             }
10369             row.cn.push(td);
10370            
10371         }
10372         
10373         row.cellObjects = cellObjects;
10374         
10375         return row;
10376           
10377     },
10378     
10379     
10380     
10381     onBeforeLoad : function()
10382     {
10383         
10384     },
10385      /**
10386      * Remove all rows
10387      */
10388     clear : function()
10389     {
10390         this.el.select('tbody', true).first().dom.innerHTML = '';
10391     },
10392     /**
10393      * Show or hide a row.
10394      * @param {Number} rowIndex to show or hide
10395      * @param {Boolean} state hide
10396      */
10397     setRowVisibility : function(rowIndex, state)
10398     {
10399         var bt = this.bodyEl.dom;
10400         
10401         var rows = this.el.select('tbody > tr', true).elements;
10402         
10403         if(typeof(rows[rowIndex]) == 'undefined'){
10404             return;
10405         }
10406         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10407         
10408     },
10409     
10410     
10411     getSelectionModel : function(){
10412         if(!this.selModel){
10413             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10414         }
10415         return this.selModel;
10416     },
10417     /*
10418      * Render the Roo.bootstrap object from renderder
10419      */
10420     renderCellObject : function(r)
10421     {
10422         var _this = this;
10423         
10424         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10425         
10426         var t = r.cfg.render(r.container);
10427         
10428         if(r.cfg.cn){
10429             Roo.each(r.cfg.cn, function(c){
10430                 var child = {
10431                     container: t.getChildContainer(),
10432                     cfg: c
10433                 };
10434                 _this.renderCellObject(child);
10435             })
10436         }
10437     },
10438     /**
10439      * get the Row Index from a dom element.
10440      * @param {Roo.Element} row The row to look for
10441      * @returns {Number} the row
10442      */
10443     getRowIndex : function(row)
10444     {
10445         var rowIndex = -1;
10446         
10447         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10448             if(el != row){
10449                 return;
10450             }
10451             
10452             rowIndex = index;
10453         });
10454         
10455         return rowIndex;
10456     },
10457     /**
10458      * get the header TH element for columnIndex
10459      * @param {Number} columnIndex
10460      * @returns {Roo.Element}
10461      */
10462     getHeaderIndex: function(colIndex)
10463     {
10464         var cols = this.headEl.select('th', true).elements;
10465         return cols[colIndex]; 
10466     },
10467     /**
10468      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10469      * @param {domElement} cell to look for
10470      * @returns {Number} the column
10471      */
10472     getCellIndex : function(cell)
10473     {
10474         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10475         if(id){
10476             return parseInt(id[1], 10);
10477         }
10478         return 0;
10479     },
10480      /**
10481      * Returns the grid's underlying element = used by panel.Grid
10482      * @return {Element} The element
10483      */
10484     getGridEl : function(){
10485         return this.el;
10486     },
10487      /**
10488      * Forces a resize - used by panel.Grid
10489      * @return {Element} The element
10490      */
10491     autoSize : function()
10492     {
10493         //var ctr = Roo.get(this.container.dom.parentElement);
10494         var ctr = Roo.get(this.el.dom);
10495         
10496         var thd = this.getGridEl().select('thead',true).first();
10497         var tbd = this.getGridEl().select('tbody', true).first();
10498         var tfd = this.getGridEl().select('tfoot', true).first();
10499         
10500         var cw = ctr.getWidth();
10501         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10502         
10503         if (tbd) {
10504             
10505             tbd.setWidth(ctr.getWidth());
10506             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10507             // this needs fixing for various usage - currently only hydra job advers I think..
10508             //tdb.setHeight(
10509             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10510             //); 
10511             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10512             cw -= barsize;
10513         }
10514         cw = Math.max(cw, this.totalWidth);
10515         this.getGridEl().select('tbody tr',true).setWidth(cw);
10516         this.initCSS();
10517         
10518         // resize 'expandable coloumn?
10519         
10520         return; // we doe not have a view in this design..
10521         
10522     },
10523     onBodyScroll: function()
10524     {
10525         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10526         if(this.headEl){
10527             this.headEl.setStyle({
10528                 'position' : 'relative',
10529                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10530             });
10531         }
10532         
10533         if(this.lazyLoad){
10534             
10535             var scrollHeight = this.bodyEl.dom.scrollHeight;
10536             
10537             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10538             
10539             var height = this.bodyEl.getHeight();
10540             
10541             if(scrollHeight - height == scrollTop) {
10542                 
10543                 var total = this.ds.getTotalCount();
10544                 
10545                 if(this.footer.cursor + this.footer.pageSize < total){
10546                     
10547                     this.footer.ds.load({
10548                         params : {
10549                             start : this.footer.cursor + this.footer.pageSize,
10550                             limit : this.footer.pageSize
10551                         },
10552                         add : true
10553                     });
10554                 }
10555             }
10556             
10557         }
10558     },
10559     onColumnSplitterMoved : function(i, diff)
10560     {
10561         this.userResized = true;
10562         
10563         var cm = this.colModel;
10564         
10565         var w = this.getHeaderIndex(i).getWidth() + diff;
10566         
10567         
10568         cm.setColumnWidth(i, w, true);
10569         this.initCSS();
10570         //var cid = cm.getColumnId(i); << not used in this version?
10571        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10572         
10573         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10574         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10575         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10576 */
10577         //this.updateSplitters();
10578         //this.layout(); << ??
10579         this.fireEvent("columnresize", i, w);
10580     },
10581     onHeaderChange : function()
10582     {
10583         var header = this.renderHeader();
10584         var table = this.el.select('table', true).first();
10585         
10586         this.headEl.remove();
10587         this.headEl = table.createChild(header, this.bodyEl, false);
10588         
10589         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10590             e.on('click', this.sort, this);
10591         }, this);
10592         
10593         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10594             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10595         }
10596         
10597     },
10598     
10599     onHiddenChange : function(colModel, colIndex, hidden)
10600     {
10601         /*
10602         this.cm.setHidden()
10603         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10604         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10605         
10606         this.CSS.updateRule(thSelector, "display", "");
10607         this.CSS.updateRule(tdSelector, "display", "");
10608         
10609         if(hidden){
10610             this.CSS.updateRule(thSelector, "display", "none");
10611             this.CSS.updateRule(tdSelector, "display", "none");
10612         }
10613         */
10614         // onload calls initCSS()
10615         this.onHeaderChange();
10616         this.onLoad();
10617     },
10618     
10619     setColumnWidth: function(col_index, width)
10620     {
10621         // width = "md-2 xs-2..."
10622         if(!this.colModel.config[col_index]) {
10623             return;
10624         }
10625         
10626         var w = width.split(" ");
10627         
10628         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10629         
10630         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10631         
10632         
10633         for(var j = 0; j < w.length; j++) {
10634             
10635             if(!w[j]) {
10636                 continue;
10637             }
10638             
10639             var size_cls = w[j].split("-");
10640             
10641             if(!Number.isInteger(size_cls[1] * 1)) {
10642                 continue;
10643             }
10644             
10645             if(!this.colModel.config[col_index][size_cls[0]]) {
10646                 continue;
10647             }
10648             
10649             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10650                 continue;
10651             }
10652             
10653             h_row[0].classList.replace(
10654                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10655                 "col-"+size_cls[0]+"-"+size_cls[1]
10656             );
10657             
10658             for(var i = 0; i < rows.length; i++) {
10659                 
10660                 var size_cls = w[j].split("-");
10661                 
10662                 if(!Number.isInteger(size_cls[1] * 1)) {
10663                     continue;
10664                 }
10665                 
10666                 if(!this.colModel.config[col_index][size_cls[0]]) {
10667                     continue;
10668                 }
10669                 
10670                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10671                     continue;
10672                 }
10673                 
10674                 rows[i].classList.replace(
10675                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10676                     "col-"+size_cls[0]+"-"+size_cls[1]
10677                 );
10678             }
10679             
10680             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10681         }
10682     }
10683 });
10684
10685 // currently only used to find the split on drag.. 
10686 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10687
10688 /**
10689  * @depricated
10690 */
10691 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10692 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10693 /*
10694  * - LGPL
10695  *
10696  * table cell
10697  * 
10698  */
10699
10700 /**
10701  * @class Roo.bootstrap.TableCell
10702  * @extends Roo.bootstrap.Component
10703  * @children Roo.bootstrap.Component
10704  * @parent Roo.bootstrap.TableRow
10705  * Bootstrap TableCell class
10706  * 
10707  * @cfg {String} html cell contain text
10708  * @cfg {String} cls cell class
10709  * @cfg {String} tag cell tag (td|th) default td
10710  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10711  * @cfg {String} align Aligns the content in a cell
10712  * @cfg {String} axis Categorizes cells
10713  * @cfg {String} bgcolor Specifies the background color of a cell
10714  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10715  * @cfg {Number} colspan Specifies the number of columns a cell should span
10716  * @cfg {String} headers Specifies one or more header cells a cell is related to
10717  * @cfg {Number} height Sets the height of a cell
10718  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10719  * @cfg {Number} rowspan Sets the number of rows a cell should span
10720  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10721  * @cfg {String} valign Vertical aligns the content in a cell
10722  * @cfg {Number} width Specifies the width of a cell
10723  * 
10724  * @constructor
10725  * Create a new TableCell
10726  * @param {Object} config The config object
10727  */
10728
10729 Roo.bootstrap.TableCell = function(config){
10730     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10731 };
10732
10733 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10734     
10735     html: false,
10736     cls: false,
10737     tag: false,
10738     abbr: false,
10739     align: false,
10740     axis: false,
10741     bgcolor: false,
10742     charoff: false,
10743     colspan: false,
10744     headers: false,
10745     height: false,
10746     nowrap: false,
10747     rowspan: false,
10748     scope: false,
10749     valign: false,
10750     width: false,
10751     
10752     
10753     getAutoCreate : function(){
10754         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10755         
10756         cfg = {
10757             tag: 'td'
10758         };
10759         
10760         if(this.tag){
10761             cfg.tag = this.tag;
10762         }
10763         
10764         if (this.html) {
10765             cfg.html=this.html
10766         }
10767         if (this.cls) {
10768             cfg.cls=this.cls
10769         }
10770         if (this.abbr) {
10771             cfg.abbr=this.abbr
10772         }
10773         if (this.align) {
10774             cfg.align=this.align
10775         }
10776         if (this.axis) {
10777             cfg.axis=this.axis
10778         }
10779         if (this.bgcolor) {
10780             cfg.bgcolor=this.bgcolor
10781         }
10782         if (this.charoff) {
10783             cfg.charoff=this.charoff
10784         }
10785         if (this.colspan) {
10786             cfg.colspan=this.colspan
10787         }
10788         if (this.headers) {
10789             cfg.headers=this.headers
10790         }
10791         if (this.height) {
10792             cfg.height=this.height
10793         }
10794         if (this.nowrap) {
10795             cfg.nowrap=this.nowrap
10796         }
10797         if (this.rowspan) {
10798             cfg.rowspan=this.rowspan
10799         }
10800         if (this.scope) {
10801             cfg.scope=this.scope
10802         }
10803         if (this.valign) {
10804             cfg.valign=this.valign
10805         }
10806         if (this.width) {
10807             cfg.width=this.width
10808         }
10809         
10810         
10811         return cfg;
10812     }
10813    
10814 });
10815
10816  
10817
10818  /*
10819  * - LGPL
10820  *
10821  * table row
10822  * 
10823  */
10824
10825 /**
10826  * @class Roo.bootstrap.TableRow
10827  * @extends Roo.bootstrap.Component
10828  * @children Roo.bootstrap.TableCell
10829  * @parent Roo.bootstrap.TableBody
10830  * Bootstrap TableRow class
10831  * @cfg {String} cls row class
10832  * @cfg {String} align Aligns the content in a table row
10833  * @cfg {String} bgcolor Specifies a background color for a table row
10834  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10835  * @cfg {String} valign Vertical aligns the content in a table row
10836  * 
10837  * @constructor
10838  * Create a new TableRow
10839  * @param {Object} config The config object
10840  */
10841
10842 Roo.bootstrap.TableRow = function(config){
10843     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10844 };
10845
10846 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10847     
10848     cls: false,
10849     align: false,
10850     bgcolor: false,
10851     charoff: false,
10852     valign: false,
10853     
10854     getAutoCreate : function(){
10855         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10856         
10857         cfg = {
10858             tag: 'tr'
10859         };
10860             
10861         if(this.cls){
10862             cfg.cls = this.cls;
10863         }
10864         if(this.align){
10865             cfg.align = this.align;
10866         }
10867         if(this.bgcolor){
10868             cfg.bgcolor = this.bgcolor;
10869         }
10870         if(this.charoff){
10871             cfg.charoff = this.charoff;
10872         }
10873         if(this.valign){
10874             cfg.valign = this.valign;
10875         }
10876         
10877         return cfg;
10878     }
10879    
10880 });
10881
10882  
10883
10884  /*
10885  * - LGPL
10886  *
10887  * table body
10888  * 
10889  */
10890
10891 /**
10892  * @class Roo.bootstrap.TableBody
10893  * @extends Roo.bootstrap.Component
10894  * @children Roo.bootstrap.TableRow
10895  * @parent Roo.bootstrap.Table
10896  * Bootstrap TableBody class
10897  * @cfg {String} cls element class
10898  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10899  * @cfg {String} align Aligns the content inside the element
10900  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10901  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10902  * 
10903  * @constructor
10904  * Create a new TableBody
10905  * @param {Object} config The config object
10906  */
10907
10908 Roo.bootstrap.TableBody = function(config){
10909     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10910 };
10911
10912 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10913     
10914     cls: false,
10915     tag: false,
10916     align: false,
10917     charoff: false,
10918     valign: false,
10919     
10920     getAutoCreate : function(){
10921         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10922         
10923         cfg = {
10924             tag: 'tbody'
10925         };
10926             
10927         if (this.cls) {
10928             cfg.cls=this.cls
10929         }
10930         if(this.tag){
10931             cfg.tag = this.tag;
10932         }
10933         
10934         if(this.align){
10935             cfg.align = this.align;
10936         }
10937         if(this.charoff){
10938             cfg.charoff = this.charoff;
10939         }
10940         if(this.valign){
10941             cfg.valign = this.valign;
10942         }
10943         
10944         return cfg;
10945     }
10946     
10947     
10948 //    initEvents : function()
10949 //    {
10950 //        
10951 //        if(!this.store){
10952 //            return;
10953 //        }
10954 //        
10955 //        this.store = Roo.factory(this.store, Roo.data);
10956 //        this.store.on('load', this.onLoad, this);
10957 //        
10958 //        this.store.load();
10959 //        
10960 //    },
10961 //    
10962 //    onLoad: function () 
10963 //    {   
10964 //        this.fireEvent('load', this);
10965 //    }
10966 //    
10967 //   
10968 });
10969
10970  
10971
10972  /*
10973  * Based on:
10974  * Ext JS Library 1.1.1
10975  * Copyright(c) 2006-2007, Ext JS, LLC.
10976  *
10977  * Originally Released Under LGPL - original licence link has changed is not relivant.
10978  *
10979  * Fork - LGPL
10980  * <script type="text/javascript">
10981  */
10982
10983 // as we use this in bootstrap.
10984 Roo.namespace('Roo.form');
10985  /**
10986  * @class Roo.form.Action
10987  * Internal Class used to handle form actions
10988  * @constructor
10989  * @param {Roo.form.BasicForm} el The form element or its id
10990  * @param {Object} config Configuration options
10991  */
10992
10993  
10994  
10995 // define the action interface
10996 Roo.form.Action = function(form, options){
10997     this.form = form;
10998     this.options = options || {};
10999 };
11000 /**
11001  * Client Validation Failed
11002  * @const 
11003  */
11004 Roo.form.Action.CLIENT_INVALID = 'client';
11005 /**
11006  * Server Validation Failed
11007  * @const 
11008  */
11009 Roo.form.Action.SERVER_INVALID = 'server';
11010  /**
11011  * Connect to Server Failed
11012  * @const 
11013  */
11014 Roo.form.Action.CONNECT_FAILURE = 'connect';
11015 /**
11016  * Reading Data from Server Failed
11017  * @const 
11018  */
11019 Roo.form.Action.LOAD_FAILURE = 'load';
11020
11021 Roo.form.Action.prototype = {
11022     type : 'default',
11023     failureType : undefined,
11024     response : undefined,
11025     result : undefined,
11026
11027     // interface method
11028     run : function(options){
11029
11030     },
11031
11032     // interface method
11033     success : function(response){
11034
11035     },
11036
11037     // interface method
11038     handleResponse : function(response){
11039
11040     },
11041
11042     // default connection failure
11043     failure : function(response){
11044         
11045         this.response = response;
11046         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11047         this.form.afterAction(this, false);
11048     },
11049
11050     processResponse : function(response){
11051         this.response = response;
11052         if(!response.responseText){
11053             return true;
11054         }
11055         this.result = this.handleResponse(response);
11056         return this.result;
11057     },
11058
11059     // utility functions used internally
11060     getUrl : function(appendParams){
11061         var url = this.options.url || this.form.url || this.form.el.dom.action;
11062         if(appendParams){
11063             var p = this.getParams();
11064             if(p){
11065                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11066             }
11067         }
11068         return url;
11069     },
11070
11071     getMethod : function(){
11072         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11073     },
11074
11075     getParams : function(){
11076         var bp = this.form.baseParams;
11077         var p = this.options.params;
11078         if(p){
11079             if(typeof p == "object"){
11080                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11081             }else if(typeof p == 'string' && bp){
11082                 p += '&' + Roo.urlEncode(bp);
11083             }
11084         }else if(bp){
11085             p = Roo.urlEncode(bp);
11086         }
11087         return p;
11088     },
11089
11090     createCallback : function(){
11091         return {
11092             success: this.success,
11093             failure: this.failure,
11094             scope: this,
11095             timeout: (this.form.timeout*1000),
11096             upload: this.form.fileUpload ? this.success : undefined
11097         };
11098     }
11099 };
11100
11101 Roo.form.Action.Submit = function(form, options){
11102     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11103 };
11104
11105 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11106     type : 'submit',
11107
11108     haveProgress : false,
11109     uploadComplete : false,
11110     
11111     // uploadProgress indicator.
11112     uploadProgress : function()
11113     {
11114         if (!this.form.progressUrl) {
11115             return;
11116         }
11117         
11118         if (!this.haveProgress) {
11119             Roo.MessageBox.progress("Uploading", "Uploading");
11120         }
11121         if (this.uploadComplete) {
11122            Roo.MessageBox.hide();
11123            return;
11124         }
11125         
11126         this.haveProgress = true;
11127    
11128         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11129         
11130         var c = new Roo.data.Connection();
11131         c.request({
11132             url : this.form.progressUrl,
11133             params: {
11134                 id : uid
11135             },
11136             method: 'GET',
11137             success : function(req){
11138                //console.log(data);
11139                 var rdata = false;
11140                 var edata;
11141                 try  {
11142                    rdata = Roo.decode(req.responseText)
11143                 } catch (e) {
11144                     Roo.log("Invalid data from server..");
11145                     Roo.log(edata);
11146                     return;
11147                 }
11148                 if (!rdata || !rdata.success) {
11149                     Roo.log(rdata);
11150                     Roo.MessageBox.alert(Roo.encode(rdata));
11151                     return;
11152                 }
11153                 var data = rdata.data;
11154                 
11155                 if (this.uploadComplete) {
11156                    Roo.MessageBox.hide();
11157                    return;
11158                 }
11159                    
11160                 if (data){
11161                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11162                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11163                     );
11164                 }
11165                 this.uploadProgress.defer(2000,this);
11166             },
11167        
11168             failure: function(data) {
11169                 Roo.log('progress url failed ');
11170                 Roo.log(data);
11171             },
11172             scope : this
11173         });
11174            
11175     },
11176     
11177     
11178     run : function()
11179     {
11180         // run get Values on the form, so it syncs any secondary forms.
11181         this.form.getValues();
11182         
11183         var o = this.options;
11184         var method = this.getMethod();
11185         var isPost = method == 'POST';
11186         if(o.clientValidation === false || this.form.isValid()){
11187             
11188             if (this.form.progressUrl) {
11189                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11190                     (new Date() * 1) + '' + Math.random());
11191                     
11192             } 
11193             
11194             
11195             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11196                 form:this.form.el.dom,
11197                 url:this.getUrl(!isPost),
11198                 method: method,
11199                 params:isPost ? this.getParams() : null,
11200                 isUpload: this.form.fileUpload,
11201                 formData : this.form.formData
11202             }));
11203             
11204             this.uploadProgress();
11205
11206         }else if (o.clientValidation !== false){ // client validation failed
11207             this.failureType = Roo.form.Action.CLIENT_INVALID;
11208             this.form.afterAction(this, false);
11209         }
11210     },
11211
11212     success : function(response)
11213     {
11214         this.uploadComplete= true;
11215         if (this.haveProgress) {
11216             Roo.MessageBox.hide();
11217         }
11218         
11219         
11220         var result = this.processResponse(response);
11221         if(result === true || result.success){
11222             this.form.afterAction(this, true);
11223             return;
11224         }
11225         if(result.errors){
11226             this.form.markInvalid(result.errors);
11227             this.failureType = Roo.form.Action.SERVER_INVALID;
11228         }
11229         this.form.afterAction(this, false);
11230     },
11231     failure : function(response)
11232     {
11233         this.uploadComplete= true;
11234         if (this.haveProgress) {
11235             Roo.MessageBox.hide();
11236         }
11237         
11238         this.response = response;
11239         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11240         this.form.afterAction(this, false);
11241     },
11242     
11243     handleResponse : function(response){
11244         if(this.form.errorReader){
11245             var rs = this.form.errorReader.read(response);
11246             var errors = [];
11247             if(rs.records){
11248                 for(var i = 0, len = rs.records.length; i < len; i++) {
11249                     var r = rs.records[i];
11250                     errors[i] = r.data;
11251                 }
11252             }
11253             if(errors.length < 1){
11254                 errors = null;
11255             }
11256             return {
11257                 success : rs.success,
11258                 errors : errors
11259             };
11260         }
11261         var ret = false;
11262         try {
11263             ret = Roo.decode(response.responseText);
11264         } catch (e) {
11265             ret = {
11266                 success: false,
11267                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11268                 errors : []
11269             };
11270         }
11271         return ret;
11272         
11273     }
11274 });
11275
11276
11277 Roo.form.Action.Load = function(form, options){
11278     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11279     this.reader = this.form.reader;
11280 };
11281
11282 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11283     type : 'load',
11284
11285     run : function(){
11286         
11287         Roo.Ajax.request(Roo.apply(
11288                 this.createCallback(), {
11289                     method:this.getMethod(),
11290                     url:this.getUrl(false),
11291                     params:this.getParams()
11292         }));
11293     },
11294
11295     success : function(response){
11296         
11297         var result = this.processResponse(response);
11298         if(result === true || !result.success || !result.data){
11299             this.failureType = Roo.form.Action.LOAD_FAILURE;
11300             this.form.afterAction(this, false);
11301             return;
11302         }
11303         this.form.clearInvalid();
11304         this.form.setValues(result.data);
11305         this.form.afterAction(this, true);
11306     },
11307
11308     handleResponse : function(response){
11309         if(this.form.reader){
11310             var rs = this.form.reader.read(response);
11311             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11312             return {
11313                 success : rs.success,
11314                 data : data
11315             };
11316         }
11317         return Roo.decode(response.responseText);
11318     }
11319 });
11320
11321 Roo.form.Action.ACTION_TYPES = {
11322     'load' : Roo.form.Action.Load,
11323     'submit' : Roo.form.Action.Submit
11324 };/*
11325  * - LGPL
11326  *
11327  * form
11328  *
11329  */
11330
11331 /**
11332  * @class Roo.bootstrap.form.Form
11333  * @extends Roo.bootstrap.Component
11334  * @children Roo.bootstrap.Component
11335  * Bootstrap Form class
11336  * @cfg {String} method  GET | POST (default POST)
11337  * @cfg {String} labelAlign top | left (default top)
11338  * @cfg {String} align left  | right - for navbars
11339  * @cfg {Boolean} loadMask load mask when submit (default true)
11340
11341  *
11342  * @constructor
11343  * Create a new Form
11344  * @param {Object} config The config object
11345  */
11346
11347
11348 Roo.bootstrap.form.Form = function(config){
11349     
11350     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11351     
11352     Roo.bootstrap.form.Form.popover.apply();
11353     
11354     this.addEvents({
11355         /**
11356          * @event clientvalidation
11357          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11358          * @param {Form} this
11359          * @param {Boolean} valid true if the form has passed client-side validation
11360          */
11361         clientvalidation: true,
11362         /**
11363          * @event beforeaction
11364          * Fires before any action is performed. Return false to cancel the action.
11365          * @param {Form} this
11366          * @param {Action} action The action to be performed
11367          */
11368         beforeaction: true,
11369         /**
11370          * @event actionfailed
11371          * Fires when an action fails.
11372          * @param {Form} this
11373          * @param {Action} action The action that failed
11374          */
11375         actionfailed : true,
11376         /**
11377          * @event actioncomplete
11378          * Fires when an action is completed.
11379          * @param {Form} this
11380          * @param {Action} action The action that completed
11381          */
11382         actioncomplete : true
11383     });
11384 };
11385
11386 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11387
11388      /**
11389      * @cfg {String} method
11390      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11391      */
11392     method : 'POST',
11393     /**
11394      * @cfg {String} url
11395      * The URL to use for form actions if one isn't supplied in the action options.
11396      */
11397     /**
11398      * @cfg {Boolean} fileUpload
11399      * Set to true if this form is a file upload.
11400      */
11401
11402     /**
11403      * @cfg {Object} baseParams
11404      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11405      */
11406
11407     /**
11408      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11409      */
11410     timeout: 30,
11411     /**
11412      * @cfg {Sting} align (left|right) for navbar forms
11413      */
11414     align : 'left',
11415
11416     // private
11417     activeAction : null,
11418
11419     /**
11420      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11421      * element by passing it or its id or mask the form itself by passing in true.
11422      * @type Mixed
11423      */
11424     waitMsgTarget : false,
11425
11426     loadMask : true,
11427     
11428     /**
11429      * @cfg {Boolean} errorMask (true|false) default false
11430      */
11431     errorMask : false,
11432     
11433     /**
11434      * @cfg {Number} maskOffset Default 100
11435      */
11436     maskOffset : 100,
11437     
11438     /**
11439      * @cfg {Boolean} maskBody
11440      */
11441     maskBody : false,
11442
11443     getAutoCreate : function(){
11444
11445         var cfg = {
11446             tag: 'form',
11447             method : this.method || 'POST',
11448             id : this.id || Roo.id(),
11449             cls : ''
11450         };
11451         if (this.parent().xtype.match(/^Nav/)) {
11452             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11453
11454         }
11455
11456         if (this.labelAlign == 'left' ) {
11457             cfg.cls += ' form-horizontal';
11458         }
11459
11460
11461         return cfg;
11462     },
11463     initEvents : function()
11464     {
11465         this.el.on('submit', this.onSubmit, this);
11466         // this was added as random key presses on the form where triggering form submit.
11467         this.el.on('keypress', function(e) {
11468             if (e.getCharCode() != 13) {
11469                 return true;
11470             }
11471             // we might need to allow it for textareas.. and some other items.
11472             // check e.getTarget().
11473
11474             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11475                 return true;
11476             }
11477
11478             Roo.log("keypress blocked");
11479
11480             e.preventDefault();
11481             return false;
11482         });
11483         
11484     },
11485     // private
11486     onSubmit : function(e){
11487         e.stopEvent();
11488     },
11489
11490      /**
11491      * Returns true if client-side validation on the form is successful.
11492      * @return Boolean
11493      */
11494     isValid : function(){
11495         var items = this.getItems();
11496         var valid = true;
11497         var target = false;
11498         
11499         items.each(function(f){
11500             
11501             if(f.validate()){
11502                 return;
11503             }
11504             
11505             Roo.log('invalid field: ' + f.name);
11506             
11507             valid = false;
11508
11509             if(!target && f.el.isVisible(true)){
11510                 target = f;
11511             }
11512            
11513         });
11514         
11515         if(this.errorMask && !valid){
11516             Roo.bootstrap.form.Form.popover.mask(this, target);
11517         }
11518         
11519         return valid;
11520     },
11521     
11522     /**
11523      * Returns true if any fields in this form have changed since their original load.
11524      * @return Boolean
11525      */
11526     isDirty : function(){
11527         var dirty = false;
11528         var items = this.getItems();
11529         items.each(function(f){
11530            if(f.isDirty()){
11531                dirty = true;
11532                return false;
11533            }
11534            return true;
11535         });
11536         return dirty;
11537     },
11538      /**
11539      * Performs a predefined action (submit or load) or custom actions you define on this form.
11540      * @param {String} actionName The name of the action type
11541      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11542      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11543      * accept other config options):
11544      * <pre>
11545 Property          Type             Description
11546 ----------------  ---------------  ----------------------------------------------------------------------------------
11547 url               String           The url for the action (defaults to the form's url)
11548 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11549 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11550 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11551                                    validate the form on the client (defaults to false)
11552      * </pre>
11553      * @return {BasicForm} this
11554      */
11555     doAction : function(action, options){
11556         if(typeof action == 'string'){
11557             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11558         }
11559         if(this.fireEvent('beforeaction', this, action) !== false){
11560             this.beforeAction(action);
11561             action.run.defer(100, action);
11562         }
11563         return this;
11564     },
11565
11566     // private
11567     beforeAction : function(action){
11568         var o = action.options;
11569         
11570         if(this.loadMask){
11571             
11572             if(this.maskBody){
11573                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11574             } else {
11575                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11576             }
11577         }
11578         // not really supported yet.. ??
11579
11580         //if(this.waitMsgTarget === true){
11581         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11582         //}else if(this.waitMsgTarget){
11583         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11584         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11585         //}else {
11586         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11587        // }
11588
11589     },
11590
11591     // private
11592     afterAction : function(action, success){
11593         this.activeAction = null;
11594         var o = action.options;
11595
11596         if(this.loadMask){
11597             
11598             if(this.maskBody){
11599                 Roo.get(document.body).unmask();
11600             } else {
11601                 this.el.unmask();
11602             }
11603         }
11604         
11605         //if(this.waitMsgTarget === true){
11606 //            this.el.unmask();
11607         //}else if(this.waitMsgTarget){
11608         //    this.waitMsgTarget.unmask();
11609         //}else{
11610         //    Roo.MessageBox.updateProgress(1);
11611         //    Roo.MessageBox.hide();
11612        // }
11613         //
11614         if(success){
11615             if(o.reset){
11616                 this.reset();
11617             }
11618             Roo.callback(o.success, o.scope, [this, action]);
11619             this.fireEvent('actioncomplete', this, action);
11620
11621         }else{
11622
11623             // failure condition..
11624             // we have a scenario where updates need confirming.
11625             // eg. if a locking scenario exists..
11626             // we look for { errors : { needs_confirm : true }} in the response.
11627             if (
11628                 (typeof(action.result) != 'undefined')  &&
11629                 (typeof(action.result.errors) != 'undefined')  &&
11630                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11631            ){
11632                 var _t = this;
11633                 Roo.log("not supported yet");
11634                  /*
11635
11636                 Roo.MessageBox.confirm(
11637                     "Change requires confirmation",
11638                     action.result.errorMsg,
11639                     function(r) {
11640                         if (r != 'yes') {
11641                             return;
11642                         }
11643                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11644                     }
11645
11646                 );
11647                 */
11648
11649
11650                 return;
11651             }
11652
11653             Roo.callback(o.failure, o.scope, [this, action]);
11654             // show an error message if no failed handler is set..
11655             if (!this.hasListener('actionfailed')) {
11656                 Roo.log("need to add dialog support");
11657                 /*
11658                 Roo.MessageBox.alert("Error",
11659                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11660                         action.result.errorMsg :
11661                         "Saving Failed, please check your entries or try again"
11662                 );
11663                 */
11664             }
11665
11666             this.fireEvent('actionfailed', this, action);
11667         }
11668
11669     },
11670     /**
11671      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11672      * @param {String} id The value to search for
11673      * @return Field
11674      */
11675     findField : function(id){
11676         var items = this.getItems();
11677         var field = items.get(id);
11678         if(!field){
11679              items.each(function(f){
11680                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11681                     field = f;
11682                     return false;
11683                 }
11684                 return true;
11685             });
11686         }
11687         return field || null;
11688     },
11689      /**
11690      * Mark fields in this form invalid in bulk.
11691      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11692      * @return {BasicForm} this
11693      */
11694     markInvalid : function(errors){
11695         if(errors instanceof Array){
11696             for(var i = 0, len = errors.length; i < len; i++){
11697                 var fieldError = errors[i];
11698                 var f = this.findField(fieldError.id);
11699                 if(f){
11700                     f.markInvalid(fieldError.msg);
11701                 }
11702             }
11703         }else{
11704             var field, id;
11705             for(id in errors){
11706                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11707                     field.markInvalid(errors[id]);
11708                 }
11709             }
11710         }
11711         //Roo.each(this.childForms || [], function (f) {
11712         //    f.markInvalid(errors);
11713         //});
11714
11715         return this;
11716     },
11717
11718     /**
11719      * Set values for fields in this form in bulk.
11720      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11721      * @return {BasicForm} this
11722      */
11723     setValues : function(values){
11724         if(values instanceof Array){ // array of objects
11725             for(var i = 0, len = values.length; i < len; i++){
11726                 var v = values[i];
11727                 var f = this.findField(v.id);
11728                 if(f){
11729                     f.setValue(v.value);
11730                     if(this.trackResetOnLoad){
11731                         f.originalValue = f.getValue();
11732                     }
11733                 }
11734             }
11735         }else{ // object hash
11736             var field, id;
11737             for(id in values){
11738                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11739
11740                     if (field.setFromData &&
11741                         field.valueField &&
11742                         field.displayField &&
11743                         // combos' with local stores can
11744                         // be queried via setValue()
11745                         // to set their value..
11746                         (field.store && !field.store.isLocal)
11747                         ) {
11748                         // it's a combo
11749                         var sd = { };
11750                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11751                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11752                         field.setFromData(sd);
11753
11754                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11755                         
11756                         field.setFromData(values);
11757                         
11758                     } else {
11759                         field.setValue(values[id]);
11760                     }
11761
11762
11763                     if(this.trackResetOnLoad){
11764                         field.originalValue = field.getValue();
11765                     }
11766                 }
11767             }
11768         }
11769
11770         //Roo.each(this.childForms || [], function (f) {
11771         //    f.setValues(values);
11772         //});
11773
11774         return this;
11775     },
11776
11777     /**
11778      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11779      * they are returned as an array.
11780      * @param {Boolean} asString
11781      * @return {Object}
11782      */
11783     getValues : function(asString){
11784         //if (this.childForms) {
11785             // copy values from the child forms
11786         //    Roo.each(this.childForms, function (f) {
11787         //        this.setValues(f.getValues());
11788         //    }, this);
11789         //}
11790
11791
11792
11793         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11794         if(asString === true){
11795             return fs;
11796         }
11797         return Roo.urlDecode(fs);
11798     },
11799
11800     /**
11801      * Returns the fields in this form as an object with key/value pairs.
11802      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11803      * @return {Object}
11804      */
11805     getFieldValues : function(with_hidden)
11806     {
11807         var items = this.getItems();
11808         var ret = {};
11809         items.each(function(f){
11810             
11811             if (!f.getName()) {
11812                 return;
11813             }
11814             
11815             var v = f.getValue();
11816             
11817             if (f.inputType =='radio') {
11818                 if (typeof(ret[f.getName()]) == 'undefined') {
11819                     ret[f.getName()] = ''; // empty..
11820                 }
11821
11822                 if (!f.el.dom.checked) {
11823                     return;
11824
11825                 }
11826                 v = f.el.dom.value;
11827
11828             }
11829             
11830             if(f.xtype == 'MoneyField'){
11831                 ret[f.currencyName] = f.getCurrency();
11832             }
11833
11834             // not sure if this supported any more..
11835             if ((typeof(v) == 'object') && f.getRawValue) {
11836                 v = f.getRawValue() ; // dates..
11837             }
11838             // combo boxes where name != hiddenName...
11839             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11840                 ret[f.name] = f.getRawValue();
11841             }
11842             ret[f.getName()] = v;
11843         });
11844
11845         return ret;
11846     },
11847
11848     /**
11849      * Clears all invalid messages in this form.
11850      * @return {BasicForm} this
11851      */
11852     clearInvalid : function(){
11853         var items = this.getItems();
11854
11855         items.each(function(f){
11856            f.clearInvalid();
11857         });
11858
11859         return this;
11860     },
11861
11862     /**
11863      * Resets this form.
11864      * @return {BasicForm} this
11865      */
11866     reset : function(){
11867         var items = this.getItems();
11868         items.each(function(f){
11869             f.reset();
11870         });
11871
11872         Roo.each(this.childForms || [], function (f) {
11873             f.reset();
11874         });
11875
11876
11877         return this;
11878     },
11879     
11880     getItems : function()
11881     {
11882         var r=new Roo.util.MixedCollection(false, function(o){
11883             return o.id || (o.id = Roo.id());
11884         });
11885         var iter = function(el) {
11886             if (el.inputEl) {
11887                 r.add(el);
11888             }
11889             if (!el.items) {
11890                 return;
11891             }
11892             Roo.each(el.items,function(e) {
11893                 iter(e);
11894             });
11895         };
11896
11897         iter(this);
11898         return r;
11899     },
11900     
11901     hideFields : function(items)
11902     {
11903         Roo.each(items, function(i){
11904             
11905             var f = this.findField(i);
11906             
11907             if(!f){
11908                 return;
11909             }
11910             
11911             f.hide();
11912             
11913         }, this);
11914     },
11915     
11916     showFields : function(items)
11917     {
11918         Roo.each(items, function(i){
11919             
11920             var f = this.findField(i);
11921             
11922             if(!f){
11923                 return;
11924             }
11925             
11926             f.show();
11927             
11928         }, this);
11929     }
11930
11931 });
11932
11933 Roo.apply(Roo.bootstrap.form.Form, {
11934     
11935     popover : {
11936         
11937         padding : 5,
11938         
11939         isApplied : false,
11940         
11941         isMasked : false,
11942         
11943         form : false,
11944         
11945         target : false,
11946         
11947         toolTip : false,
11948         
11949         intervalID : false,
11950         
11951         maskEl : false,
11952         
11953         apply : function()
11954         {
11955             if(this.isApplied){
11956                 return;
11957             }
11958             
11959             this.maskEl = {
11960                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11961                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11962                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11963                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11964             };
11965             
11966             this.maskEl.top.enableDisplayMode("block");
11967             this.maskEl.left.enableDisplayMode("block");
11968             this.maskEl.bottom.enableDisplayMode("block");
11969             this.maskEl.right.enableDisplayMode("block");
11970             
11971             this.toolTip = new Roo.bootstrap.Tooltip({
11972                 cls : 'roo-form-error-popover',
11973                 alignment : {
11974                     'left' : ['r-l', [-2,0], 'right'],
11975                     'right' : ['l-r', [2,0], 'left'],
11976                     'bottom' : ['tl-bl', [0,2], 'top'],
11977                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11978                 }
11979             });
11980             
11981             this.toolTip.render(Roo.get(document.body));
11982
11983             this.toolTip.el.enableDisplayMode("block");
11984             
11985             Roo.get(document.body).on('click', function(){
11986                 this.unmask();
11987             }, this);
11988             
11989             Roo.get(document.body).on('touchstart', function(){
11990                 this.unmask();
11991             }, this);
11992             
11993             this.isApplied = true
11994         },
11995         
11996         mask : function(form, target)
11997         {
11998             this.form = form;
11999             
12000             this.target = target;
12001             
12002             if(!this.form.errorMask || !target.el){
12003                 return;
12004             }
12005             
12006             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12007             
12008             Roo.log(scrollable);
12009             
12010             var ot = this.target.el.calcOffsetsTo(scrollable);
12011             
12012             var scrollTo = ot[1] - this.form.maskOffset;
12013             
12014             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12015             
12016             scrollable.scrollTo('top', scrollTo);
12017             
12018             var box = this.target.el.getBox();
12019             Roo.log(box);
12020             var zIndex = Roo.bootstrap.Modal.zIndex++;
12021
12022             
12023             this.maskEl.top.setStyle('position', 'absolute');
12024             this.maskEl.top.setStyle('z-index', zIndex);
12025             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12026             this.maskEl.top.setLeft(0);
12027             this.maskEl.top.setTop(0);
12028             this.maskEl.top.show();
12029             
12030             this.maskEl.left.setStyle('position', 'absolute');
12031             this.maskEl.left.setStyle('z-index', zIndex);
12032             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12033             this.maskEl.left.setLeft(0);
12034             this.maskEl.left.setTop(box.y - this.padding);
12035             this.maskEl.left.show();
12036
12037             this.maskEl.bottom.setStyle('position', 'absolute');
12038             this.maskEl.bottom.setStyle('z-index', zIndex);
12039             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12040             this.maskEl.bottom.setLeft(0);
12041             this.maskEl.bottom.setTop(box.bottom + this.padding);
12042             this.maskEl.bottom.show();
12043
12044             this.maskEl.right.setStyle('position', 'absolute');
12045             this.maskEl.right.setStyle('z-index', zIndex);
12046             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12047             this.maskEl.right.setLeft(box.right + this.padding);
12048             this.maskEl.right.setTop(box.y - this.padding);
12049             this.maskEl.right.show();
12050
12051             this.toolTip.bindEl = this.target.el;
12052
12053             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12054
12055             var tip = this.target.blankText;
12056
12057             if(this.target.getValue() !== '' ) {
12058                 
12059                 if (this.target.invalidText.length) {
12060                     tip = this.target.invalidText;
12061                 } else if (this.target.regexText.length){
12062                     tip = this.target.regexText;
12063                 }
12064             }
12065
12066             this.toolTip.show(tip);
12067
12068             this.intervalID = window.setInterval(function() {
12069                 Roo.bootstrap.form.Form.popover.unmask();
12070             }, 10000);
12071
12072             window.onwheel = function(){ return false;};
12073             
12074             (function(){ this.isMasked = true; }).defer(500, this);
12075             
12076         },
12077         
12078         unmask : function()
12079         {
12080             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12081                 return;
12082             }
12083             
12084             this.maskEl.top.setStyle('position', 'absolute');
12085             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12086             this.maskEl.top.hide();
12087
12088             this.maskEl.left.setStyle('position', 'absolute');
12089             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12090             this.maskEl.left.hide();
12091
12092             this.maskEl.bottom.setStyle('position', 'absolute');
12093             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12094             this.maskEl.bottom.hide();
12095
12096             this.maskEl.right.setStyle('position', 'absolute');
12097             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12098             this.maskEl.right.hide();
12099             
12100             this.toolTip.hide();
12101             
12102             this.toolTip.el.hide();
12103             
12104             window.onwheel = function(){ return true;};
12105             
12106             if(this.intervalID){
12107                 window.clearInterval(this.intervalID);
12108                 this.intervalID = false;
12109             }
12110             
12111             this.isMasked = false;
12112             
12113         }
12114         
12115     }
12116     
12117 });
12118
12119 /*
12120  * Based on:
12121  * Ext JS Library 1.1.1
12122  * Copyright(c) 2006-2007, Ext JS, LLC.
12123  *
12124  * Originally Released Under LGPL - original licence link has changed is not relivant.
12125  *
12126  * Fork - LGPL
12127  * <script type="text/javascript">
12128  */
12129 /**
12130  * @class Roo.form.VTypes
12131  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12132  * @static
12133  */
12134 Roo.form.VTypes = function(){
12135     // closure these in so they are only created once.
12136     var alpha = /^[a-zA-Z_]+$/;
12137     var alphanum = /^[a-zA-Z0-9_]+$/;
12138     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12139     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12140
12141     // All these messages and functions are configurable
12142     return {
12143         /**
12144          * The function used to validate email addresses
12145          * @param {String} value The email address
12146          */
12147         'email' : function(v){
12148             return email.test(v);
12149         },
12150         /**
12151          * The error text to display when the email validation function returns false
12152          * @type String
12153          */
12154         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12155         /**
12156          * The keystroke filter mask to be applied on email input
12157          * @type RegExp
12158          */
12159         'emailMask' : /[a-z0-9_\.\-@]/i,
12160
12161         /**
12162          * The function used to validate URLs
12163          * @param {String} value The URL
12164          */
12165         'url' : function(v){
12166             return url.test(v);
12167         },
12168         /**
12169          * The error text to display when the url validation function returns false
12170          * @type String
12171          */
12172         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12173         
12174         /**
12175          * The function used to validate alpha values
12176          * @param {String} value The value
12177          */
12178         'alpha' : function(v){
12179             return alpha.test(v);
12180         },
12181         /**
12182          * The error text to display when the alpha validation function returns false
12183          * @type String
12184          */
12185         'alphaText' : 'This field should only contain letters and _',
12186         /**
12187          * The keystroke filter mask to be applied on alpha input
12188          * @type RegExp
12189          */
12190         'alphaMask' : /[a-z_]/i,
12191
12192         /**
12193          * The function used to validate alphanumeric values
12194          * @param {String} value The value
12195          */
12196         'alphanum' : function(v){
12197             return alphanum.test(v);
12198         },
12199         /**
12200          * The error text to display when the alphanumeric validation function returns false
12201          * @type String
12202          */
12203         'alphanumText' : 'This field should only contain letters, numbers and _',
12204         /**
12205          * The keystroke filter mask to be applied on alphanumeric input
12206          * @type RegExp
12207          */
12208         'alphanumMask' : /[a-z0-9_]/i
12209     };
12210 }();/*
12211  * - LGPL
12212  *
12213  * Input
12214  * 
12215  */
12216
12217 /**
12218  * @class Roo.bootstrap.form.Input
12219  * @extends Roo.bootstrap.Component
12220  * Bootstrap Input class
12221  * @cfg {Boolean} disabled is it disabled
12222  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12223  * @cfg {String} name name of the input
12224  * @cfg {string} fieldLabel - the label associated
12225  * @cfg {string} placeholder - placeholder to put in text.
12226  * @cfg {string} before - input group add on before
12227  * @cfg {string} after - input group add on after
12228  * @cfg {string} size - (lg|sm) or leave empty..
12229  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12230  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12231  * @cfg {Number} md colspan out of 12 for computer-sized screens
12232  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12233  * @cfg {string} value default value of the input
12234  * @cfg {Number} labelWidth set the width of label 
12235  * @cfg {Number} labellg set the width of label (1-12)
12236  * @cfg {Number} labelmd set the width of label (1-12)
12237  * @cfg {Number} labelsm set the width of label (1-12)
12238  * @cfg {Number} labelxs set the width of label (1-12)
12239  * @cfg {String} labelAlign (top|left)
12240  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12241  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12242  * @cfg {String} indicatorpos (left|right) default left
12243  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12244  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12245  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12246  * @cfg {Roo.bootstrap.Button} before Button to show before
12247  * @cfg {Roo.bootstrap.Button} afterButton to show before
12248  * @cfg {String} align (left|center|right) Default left
12249  * @cfg {Boolean} forceFeedback (true|false) Default false
12250  * 
12251  * @constructor
12252  * Create a new Input
12253  * @param {Object} config The config object
12254  */
12255
12256 Roo.bootstrap.form.Input = function(config){
12257     
12258     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12259     
12260     this.addEvents({
12261         /**
12262          * @event focus
12263          * Fires when this field receives input focus.
12264          * @param {Roo.form.Field} this
12265          */
12266         focus : true,
12267         /**
12268          * @event blur
12269          * Fires when this field loses input focus.
12270          * @param {Roo.form.Field} this
12271          */
12272         blur : true,
12273         /**
12274          * @event specialkey
12275          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12276          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12277          * @param {Roo.form.Field} this
12278          * @param {Roo.EventObject} e The event object
12279          */
12280         specialkey : true,
12281         /**
12282          * @event change
12283          * Fires just before the field blurs if the field value has changed.
12284          * @param {Roo.form.Field} this
12285          * @param {Mixed} newValue The new value
12286          * @param {Mixed} oldValue The original value
12287          */
12288         change : true,
12289         /**
12290          * @event invalid
12291          * Fires after the field has been marked as invalid.
12292          * @param {Roo.form.Field} this
12293          * @param {String} msg The validation message
12294          */
12295         invalid : true,
12296         /**
12297          * @event valid
12298          * Fires after the field has been validated with no errors.
12299          * @param {Roo.form.Field} this
12300          */
12301         valid : true,
12302          /**
12303          * @event keyup
12304          * Fires after the key up
12305          * @param {Roo.form.Field} this
12306          * @param {Roo.EventObject}  e The event Object
12307          */
12308         keyup : true,
12309         /**
12310          * @event paste
12311          * Fires after the user pastes into input
12312          * @param {Roo.form.Field} this
12313          * @param {Roo.EventObject}  e The event Object
12314          */
12315         paste : true
12316     });
12317 };
12318
12319 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12320      /**
12321      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12322       automatic validation (defaults to "keyup").
12323      */
12324     validationEvent : "keyup",
12325      /**
12326      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12327      */
12328     validateOnBlur : true,
12329     /**
12330      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12331      */
12332     validationDelay : 250,
12333      /**
12334      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12335      */
12336     focusClass : "x-form-focus",  // not needed???
12337     
12338        
12339     /**
12340      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12341      */
12342     invalidClass : "has-warning",
12343     
12344     /**
12345      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12346      */
12347     validClass : "has-success",
12348     
12349     /**
12350      * @cfg {Boolean} hasFeedback (true|false) default true
12351      */
12352     hasFeedback : true,
12353     
12354     /**
12355      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12356      */
12357     invalidFeedbackClass : "glyphicon-warning-sign",
12358     
12359     /**
12360      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12361      */
12362     validFeedbackClass : "glyphicon-ok",
12363     
12364     /**
12365      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12366      */
12367     selectOnFocus : false,
12368     
12369      /**
12370      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12371      */
12372     maskRe : null,
12373        /**
12374      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12375      */
12376     vtype : null,
12377     
12378       /**
12379      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12380      */
12381     disableKeyFilter : false,
12382     
12383        /**
12384      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12385      */
12386     disabled : false,
12387      /**
12388      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12389      */
12390     allowBlank : true,
12391     /**
12392      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12393      */
12394     blankText : "Please complete this mandatory field",
12395     
12396      /**
12397      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12398      */
12399     minLength : 0,
12400     /**
12401      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12402      */
12403     maxLength : Number.MAX_VALUE,
12404     /**
12405      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12406      */
12407     minLengthText : "The minimum length for this field is {0}",
12408     /**
12409      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12410      */
12411     maxLengthText : "The maximum length for this field is {0}",
12412   
12413     
12414     /**
12415      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12416      * If available, this function will be called only after the basic validators all return true, and will be passed the
12417      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12418      */
12419     validator : null,
12420     /**
12421      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12422      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12423      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12424      */
12425     regex : null,
12426     /**
12427      * @cfg {String} regexText -- Depricated - use Invalid Text
12428      */
12429     regexText : "",
12430     
12431     /**
12432      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12433      */
12434     invalidText : "",
12435     
12436     
12437     
12438     autocomplete: false,
12439     
12440     
12441     fieldLabel : '',
12442     inputType : 'text',
12443     
12444     name : false,
12445     placeholder: false,
12446     before : false,
12447     after : false,
12448     size : false,
12449     hasFocus : false,
12450     preventMark: false,
12451     isFormField : true,
12452     value : '',
12453     labelWidth : 2,
12454     labelAlign : false,
12455     readOnly : false,
12456     align : false,
12457     formatedValue : false,
12458     forceFeedback : false,
12459     
12460     indicatorpos : 'left',
12461     
12462     labellg : 0,
12463     labelmd : 0,
12464     labelsm : 0,
12465     labelxs : 0,
12466     
12467     capture : '',
12468     accept : '',
12469     
12470     parentLabelAlign : function()
12471     {
12472         var parent = this;
12473         while (parent.parent()) {
12474             parent = parent.parent();
12475             if (typeof(parent.labelAlign) !='undefined') {
12476                 return parent.labelAlign;
12477             }
12478         }
12479         return 'left';
12480         
12481     },
12482     
12483     getAutoCreate : function()
12484     {
12485         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12486         
12487         var id = Roo.id();
12488         
12489         var cfg = {};
12490         
12491         if(this.inputType != 'hidden'){
12492             cfg.cls = 'form-group' //input-group
12493         }
12494         
12495         var input =  {
12496             tag: 'input',
12497             id : id,
12498             type : this.inputType,
12499             value : this.value,
12500             cls : 'form-control',
12501             placeholder : this.placeholder || '',
12502             autocomplete : this.autocomplete || 'new-password'
12503         };
12504         if (this.inputType == 'file') {
12505             input.style = 'overflow:hidden'; // why not in CSS?
12506         }
12507         
12508         if(this.capture.length){
12509             input.capture = this.capture;
12510         }
12511         
12512         if(this.accept.length){
12513             input.accept = this.accept + "/*";
12514         }
12515         
12516         if(this.align){
12517             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12518         }
12519         
12520         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12521             input.maxLength = this.maxLength;
12522         }
12523         
12524         if (this.disabled) {
12525             input.disabled=true;
12526         }
12527         
12528         if (this.readOnly) {
12529             input.readonly=true;
12530         }
12531         
12532         if (this.name) {
12533             input.name = this.name;
12534         }
12535         
12536         if (this.size) {
12537             input.cls += ' input-' + this.size;
12538         }
12539         
12540         var settings=this;
12541         ['xs','sm','md','lg'].map(function(size){
12542             if (settings[size]) {
12543                 cfg.cls += ' col-' + size + '-' + settings[size];
12544             }
12545         });
12546         
12547         var inputblock = input;
12548         
12549         var feedback = {
12550             tag: 'span',
12551             cls: 'glyphicon form-control-feedback'
12552         };
12553             
12554         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12555             
12556             inputblock = {
12557                 cls : 'has-feedback',
12558                 cn :  [
12559                     input,
12560                     feedback
12561                 ] 
12562             };  
12563         }
12564         
12565         if (this.before || this.after) {
12566             
12567             inputblock = {
12568                 cls : 'input-group',
12569                 cn :  [] 
12570             };
12571             
12572             if (this.before && typeof(this.before) == 'string') {
12573                 
12574                 inputblock.cn.push({
12575                     tag :'span',
12576                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12577                     html : this.before
12578                 });
12579             }
12580             if (this.before && typeof(this.before) == 'object') {
12581                 this.before = Roo.factory(this.before);
12582                 
12583                 inputblock.cn.push({
12584                     tag :'span',
12585                     cls : 'roo-input-before input-group-prepend   input-group-' +
12586                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12587                 });
12588             }
12589             
12590             inputblock.cn.push(input);
12591             
12592             if (this.after && typeof(this.after) == 'string') {
12593                 inputblock.cn.push({
12594                     tag :'span',
12595                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12596                     html : this.after
12597                 });
12598             }
12599             if (this.after && typeof(this.after) == 'object') {
12600                 this.after = Roo.factory(this.after);
12601                 
12602                 inputblock.cn.push({
12603                     tag :'span',
12604                     cls : 'roo-input-after input-group-append  input-group-' +
12605                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12606                 });
12607             }
12608             
12609             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12610                 inputblock.cls += ' has-feedback';
12611                 inputblock.cn.push(feedback);
12612             }
12613         };
12614         var indicator = {
12615             tag : 'i',
12616             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12617             tooltip : 'This field is required'
12618         };
12619         if (this.allowBlank ) {
12620             indicator.style = this.allowBlank ? ' display:none' : '';
12621         }
12622         if (align ==='left' && this.fieldLabel.length) {
12623             
12624             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12625             
12626             cfg.cn = [
12627                 indicator,
12628                 {
12629                     tag: 'label',
12630                     'for' :  id,
12631                     cls : 'control-label col-form-label',
12632                     html : this.fieldLabel
12633
12634                 },
12635                 {
12636                     cls : "", 
12637                     cn: [
12638                         inputblock
12639                     ]
12640                 }
12641             ];
12642             
12643             var labelCfg = cfg.cn[1];
12644             var contentCfg = cfg.cn[2];
12645             
12646             if(this.indicatorpos == 'right'){
12647                 cfg.cn = [
12648                     {
12649                         tag: 'label',
12650                         'for' :  id,
12651                         cls : 'control-label col-form-label',
12652                         cn : [
12653                             {
12654                                 tag : 'span',
12655                                 html : this.fieldLabel
12656                             },
12657                             indicator
12658                         ]
12659                     },
12660                     {
12661                         cls : "",
12662                         cn: [
12663                             inputblock
12664                         ]
12665                     }
12666
12667                 ];
12668                 
12669                 labelCfg = cfg.cn[0];
12670                 contentCfg = cfg.cn[1];
12671             
12672             }
12673             
12674             if(this.labelWidth > 12){
12675                 labelCfg.style = "width: " + this.labelWidth + 'px';
12676             }
12677             
12678             if(this.labelWidth < 13 && this.labelmd == 0){
12679                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12680             }
12681             
12682             if(this.labellg > 0){
12683                 labelCfg.cls += ' col-lg-' + this.labellg;
12684                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12685             }
12686             
12687             if(this.labelmd > 0){
12688                 labelCfg.cls += ' col-md-' + this.labelmd;
12689                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12690             }
12691             
12692             if(this.labelsm > 0){
12693                 labelCfg.cls += ' col-sm-' + this.labelsm;
12694                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12695             }
12696             
12697             if(this.labelxs > 0){
12698                 labelCfg.cls += ' col-xs-' + this.labelxs;
12699                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12700             }
12701             
12702             
12703         } else if ( this.fieldLabel.length) {
12704                 
12705             
12706             
12707             cfg.cn = [
12708                 {
12709                     tag : 'i',
12710                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12711                     tooltip : 'This field is required',
12712                     style : this.allowBlank ? ' display:none' : '' 
12713                 },
12714                 {
12715                     tag: 'label',
12716                    //cls : 'input-group-addon',
12717                     html : this.fieldLabel
12718
12719                 },
12720
12721                inputblock
12722
12723            ];
12724            
12725            if(this.indicatorpos == 'right'){
12726        
12727                 cfg.cn = [
12728                     {
12729                         tag: 'label',
12730                        //cls : 'input-group-addon',
12731                         html : this.fieldLabel
12732
12733                     },
12734                     {
12735                         tag : 'i',
12736                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12737                         tooltip : 'This field is required',
12738                         style : this.allowBlank ? ' display:none' : '' 
12739                     },
12740
12741                    inputblock
12742
12743                ];
12744
12745             }
12746
12747         } else {
12748             
12749             cfg.cn = [
12750
12751                     inputblock
12752
12753             ];
12754                 
12755                 
12756         };
12757         
12758         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12759            cfg.cls += ' navbar-form';
12760         }
12761         
12762         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12763             // on BS4 we do this only if not form 
12764             cfg.cls += ' navbar-form';
12765             cfg.tag = 'li';
12766         }
12767         
12768         return cfg;
12769         
12770     },
12771     /**
12772      * return the real input element.
12773      */
12774     inputEl: function ()
12775     {
12776         return this.el.select('input.form-control',true).first();
12777     },
12778     
12779     tooltipEl : function()
12780     {
12781         return this.inputEl();
12782     },
12783     
12784     indicatorEl : function()
12785     {
12786         if (Roo.bootstrap.version == 4) {
12787             return false; // not enabled in v4 yet.
12788         }
12789         
12790         var indicator = this.el.select('i.roo-required-indicator',true).first();
12791         
12792         if(!indicator){
12793             return false;
12794         }
12795         
12796         return indicator;
12797         
12798     },
12799     
12800     setDisabled : function(v)
12801     {
12802         var i  = this.inputEl().dom;
12803         if (!v) {
12804             i.removeAttribute('disabled');
12805             return;
12806             
12807         }
12808         i.setAttribute('disabled','true');
12809     },
12810     initEvents : function()
12811     {
12812           
12813         this.inputEl().on("keydown" , this.fireKey,  this);
12814         this.inputEl().on("focus", this.onFocus,  this);
12815         this.inputEl().on("blur", this.onBlur,  this);
12816         
12817         this.inputEl().relayEvent('keyup', this);
12818         this.inputEl().relayEvent('paste', this);
12819         
12820         this.indicator = this.indicatorEl();
12821         
12822         if(this.indicator){
12823             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12824         }
12825  
12826         // reference to original value for reset
12827         this.originalValue = this.getValue();
12828         //Roo.form.TextField.superclass.initEvents.call(this);
12829         if(this.validationEvent == 'keyup'){
12830             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12831             this.inputEl().on('keyup', this.filterValidation, this);
12832         }
12833         else if(this.validationEvent !== false){
12834             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12835         }
12836         
12837         if(this.selectOnFocus){
12838             this.on("focus", this.preFocus, this);
12839             
12840         }
12841         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12842             this.inputEl().on("keypress", this.filterKeys, this);
12843         } else {
12844             this.inputEl().relayEvent('keypress', this);
12845         }
12846        /* if(this.grow){
12847             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12848             this.el.on("click", this.autoSize,  this);
12849         }
12850         */
12851         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12852             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12853         }
12854         
12855         if (typeof(this.before) == 'object') {
12856             this.before.render(this.el.select('.roo-input-before',true).first());
12857         }
12858         if (typeof(this.after) == 'object') {
12859             this.after.render(this.el.select('.roo-input-after',true).first());
12860         }
12861         
12862         this.inputEl().on('change', this.onChange, this);
12863         
12864     },
12865     filterValidation : function(e){
12866         if(!e.isNavKeyPress()){
12867             this.validationTask.delay(this.validationDelay);
12868         }
12869     },
12870      /**
12871      * Validates the field value
12872      * @return {Boolean} True if the value is valid, else false
12873      */
12874     validate : function(){
12875         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12876         if(this.disabled || this.validateValue(this.getRawValue())){
12877             this.markValid();
12878             return true;
12879         }
12880         
12881         this.markInvalid();
12882         return false;
12883     },
12884     
12885     
12886     /**
12887      * Validates a value according to the field's validation rules and marks the field as invalid
12888      * if the validation fails
12889      * @param {Mixed} value The value to validate
12890      * @return {Boolean} True if the value is valid, else false
12891      */
12892     validateValue : function(value)
12893     {
12894         if(this.getVisibilityEl().hasClass('hidden')){
12895             return true;
12896         }
12897         
12898         if(value.length < 1)  { // if it's blank
12899             if(this.allowBlank){
12900                 return true;
12901             }
12902             return false;
12903         }
12904         
12905         if(value.length < this.minLength){
12906             return false;
12907         }
12908         if(value.length > this.maxLength){
12909             return false;
12910         }
12911         if(this.vtype){
12912             var vt = Roo.form.VTypes;
12913             if(!vt[this.vtype](value, this)){
12914                 return false;
12915             }
12916         }
12917         if(typeof this.validator == "function"){
12918             var msg = this.validator(value);
12919             if(msg !== true){
12920                 return false;
12921             }
12922             if (typeof(msg) == 'string') {
12923                 this.invalidText = msg;
12924             }
12925         }
12926         
12927         if(this.regex && !this.regex.test(value)){
12928             return false;
12929         }
12930         
12931         return true;
12932     },
12933     
12934      // private
12935     fireKey : function(e){
12936         //Roo.log('field ' + e.getKey());
12937         if(e.isNavKeyPress()){
12938             this.fireEvent("specialkey", this, e);
12939         }
12940     },
12941     focus : function (selectText){
12942         if(this.rendered){
12943             this.inputEl().focus();
12944             if(selectText === true){
12945                 this.inputEl().dom.select();
12946             }
12947         }
12948         return this;
12949     } ,
12950     
12951     onFocus : function(){
12952         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12953            // this.el.addClass(this.focusClass);
12954         }
12955         if(!this.hasFocus){
12956             this.hasFocus = true;
12957             this.startValue = this.getValue();
12958             this.fireEvent("focus", this);
12959         }
12960     },
12961     
12962     beforeBlur : Roo.emptyFn,
12963
12964     
12965     // private
12966     onBlur : function(){
12967         this.beforeBlur();
12968         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12969             //this.el.removeClass(this.focusClass);
12970         }
12971         this.hasFocus = false;
12972         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12973             this.validate();
12974         }
12975         var v = this.getValue();
12976         if(String(v) !== String(this.startValue)){
12977             this.fireEvent('change', this, v, this.startValue);
12978         }
12979         this.fireEvent("blur", this);
12980     },
12981     
12982     onChange : function(e)
12983     {
12984         var v = this.getValue();
12985         if(String(v) !== String(this.startValue)){
12986             this.fireEvent('change', this, v, this.startValue);
12987         }
12988         
12989     },
12990     
12991     /**
12992      * Resets the current field value to the originally loaded value and clears any validation messages
12993      */
12994     reset : function(){
12995         this.setValue(this.originalValue);
12996         this.validate();
12997     },
12998      /**
12999      * Returns the name of the field
13000      * @return {Mixed} name The name field
13001      */
13002     getName: function(){
13003         return this.name;
13004     },
13005      /**
13006      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13007      * @return {Mixed} value The field value
13008      */
13009     getValue : function(){
13010         
13011         var v = this.inputEl().getValue();
13012         
13013         return v;
13014     },
13015     /**
13016      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13017      * @return {Mixed} value The field value
13018      */
13019     getRawValue : function(){
13020         var v = this.inputEl().getValue();
13021         
13022         return v;
13023     },
13024     
13025     /**
13026      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13027      * @param {Mixed} value The value to set
13028      */
13029     setRawValue : function(v){
13030         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13031     },
13032     
13033     selectText : function(start, end){
13034         var v = this.getRawValue();
13035         if(v.length > 0){
13036             start = start === undefined ? 0 : start;
13037             end = end === undefined ? v.length : end;
13038             var d = this.inputEl().dom;
13039             if(d.setSelectionRange){
13040                 d.setSelectionRange(start, end);
13041             }else if(d.createTextRange){
13042                 var range = d.createTextRange();
13043                 range.moveStart("character", start);
13044                 range.moveEnd("character", v.length-end);
13045                 range.select();
13046             }
13047         }
13048     },
13049     
13050     /**
13051      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13052      * @param {Mixed} value The value to set
13053      */
13054     setValue : function(v){
13055         this.value = v;
13056         if(this.rendered){
13057             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13058             this.validate();
13059         }
13060     },
13061     
13062     /*
13063     processValue : function(value){
13064         if(this.stripCharsRe){
13065             var newValue = value.replace(this.stripCharsRe, '');
13066             if(newValue !== value){
13067                 this.setRawValue(newValue);
13068                 return newValue;
13069             }
13070         }
13071         return value;
13072     },
13073   */
13074     preFocus : function(){
13075         
13076         if(this.selectOnFocus){
13077             this.inputEl().dom.select();
13078         }
13079     },
13080     filterKeys : function(e){
13081         var k = e.getKey();
13082         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13083             return;
13084         }
13085         var c = e.getCharCode(), cc = String.fromCharCode(c);
13086         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13087             return;
13088         }
13089         if(!this.maskRe.test(cc)){
13090             e.stopEvent();
13091         }
13092     },
13093      /**
13094      * Clear any invalid styles/messages for this field
13095      */
13096     clearInvalid : function(){
13097         
13098         if(!this.el || this.preventMark){ // not rendered
13099             return;
13100         }
13101         
13102         
13103         this.el.removeClass([this.invalidClass, 'is-invalid']);
13104         
13105         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13106             
13107             var feedback = this.el.select('.form-control-feedback', true).first();
13108             
13109             if(feedback){
13110                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13111             }
13112             
13113         }
13114         
13115         if(this.indicator){
13116             this.indicator.removeClass('visible');
13117             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13118         }
13119         
13120         this.fireEvent('valid', this);
13121     },
13122     
13123      /**
13124      * Mark this field as valid
13125      */
13126     markValid : function()
13127     {
13128         if(!this.el  || this.preventMark){ // not rendered...
13129             return;
13130         }
13131         
13132         this.el.removeClass([this.invalidClass, this.validClass]);
13133         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13134
13135         var feedback = this.el.select('.form-control-feedback', true).first();
13136             
13137         if(feedback){
13138             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13139         }
13140         
13141         if(this.indicator){
13142             this.indicator.removeClass('visible');
13143             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13144         }
13145         
13146         if(this.disabled){
13147             return;
13148         }
13149         
13150            
13151         if(this.allowBlank && !this.getRawValue().length){
13152             return;
13153         }
13154         if (Roo.bootstrap.version == 3) {
13155             this.el.addClass(this.validClass);
13156         } else {
13157             this.inputEl().addClass('is-valid');
13158         }
13159
13160         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13161             
13162             var feedback = this.el.select('.form-control-feedback', true).first();
13163             
13164             if(feedback){
13165                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13166                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13167             }
13168             
13169         }
13170         
13171         this.fireEvent('valid', this);
13172     },
13173     
13174      /**
13175      * Mark this field as invalid
13176      * @param {String} msg The validation message
13177      */
13178     markInvalid : function(msg)
13179     {
13180         if(!this.el  || this.preventMark){ // not rendered
13181             return;
13182         }
13183         
13184         this.el.removeClass([this.invalidClass, this.validClass]);
13185         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13186         
13187         var feedback = this.el.select('.form-control-feedback', true).first();
13188             
13189         if(feedback){
13190             this.el.select('.form-control-feedback', true).first().removeClass(
13191                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13192         }
13193
13194         if(this.disabled){
13195             return;
13196         }
13197         
13198         if(this.allowBlank && !this.getRawValue().length){
13199             return;
13200         }
13201         
13202         if(this.indicator){
13203             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13204             this.indicator.addClass('visible');
13205         }
13206         if (Roo.bootstrap.version == 3) {
13207             this.el.addClass(this.invalidClass);
13208         } else {
13209             this.inputEl().addClass('is-invalid');
13210         }
13211         
13212         
13213         
13214         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13215             
13216             var feedback = this.el.select('.form-control-feedback', true).first();
13217             
13218             if(feedback){
13219                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13220                 
13221                 if(this.getValue().length || this.forceFeedback){
13222                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13223                 }
13224                 
13225             }
13226             
13227         }
13228         
13229         this.fireEvent('invalid', this, msg);
13230     },
13231     // private
13232     SafariOnKeyDown : function(event)
13233     {
13234         // this is a workaround for a password hang bug on chrome/ webkit.
13235         if (this.inputEl().dom.type != 'password') {
13236             return;
13237         }
13238         
13239         var isSelectAll = false;
13240         
13241         if(this.inputEl().dom.selectionEnd > 0){
13242             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13243         }
13244         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13245             event.preventDefault();
13246             this.setValue('');
13247             return;
13248         }
13249         
13250         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13251             
13252             event.preventDefault();
13253             // this is very hacky as keydown always get's upper case.
13254             //
13255             var cc = String.fromCharCode(event.getCharCode());
13256             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13257             
13258         }
13259     },
13260     adjustWidth : function(tag, w){
13261         tag = tag.toLowerCase();
13262         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13263             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13264                 if(tag == 'input'){
13265                     return w + 2;
13266                 }
13267                 if(tag == 'textarea'){
13268                     return w-2;
13269                 }
13270             }else if(Roo.isOpera){
13271                 if(tag == 'input'){
13272                     return w + 2;
13273                 }
13274                 if(tag == 'textarea'){
13275                     return w-2;
13276                 }
13277             }
13278         }
13279         return w;
13280     },
13281     
13282     setFieldLabel : function(v)
13283     {
13284         if(!this.rendered){
13285             return;
13286         }
13287         
13288         if(this.indicatorEl()){
13289             var ar = this.el.select('label > span',true);
13290             
13291             if (ar.elements.length) {
13292                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13293                 this.fieldLabel = v;
13294                 return;
13295             }
13296             
13297             var br = this.el.select('label',true);
13298             
13299             if(br.elements.length) {
13300                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13301                 this.fieldLabel = v;
13302                 return;
13303             }
13304             
13305             Roo.log('Cannot Found any of label > span || label in input');
13306             return;
13307         }
13308         
13309         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13310         this.fieldLabel = v;
13311         
13312         
13313     }
13314 });
13315
13316  
13317 /*
13318  * - LGPL
13319  *
13320  * Input
13321  * 
13322  */
13323
13324 /**
13325  * @class Roo.bootstrap.form.TextArea
13326  * @extends Roo.bootstrap.form.Input
13327  * Bootstrap TextArea class
13328  * @cfg {Number} cols Specifies the visible width of a text area
13329  * @cfg {Number} rows Specifies the visible number of lines in a text area
13330  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13331  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13332  * @cfg {string} html text
13333  * 
13334  * @constructor
13335  * Create a new TextArea
13336  * @param {Object} config The config object
13337  */
13338
13339 Roo.bootstrap.form.TextArea = function(config){
13340     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13341    
13342 };
13343
13344 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13345      
13346     cols : false,
13347     rows : 5,
13348     readOnly : false,
13349     warp : 'soft',
13350     resize : false,
13351     value: false,
13352     html: false,
13353     
13354     getAutoCreate : function(){
13355         
13356         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13357         
13358         var id = Roo.id();
13359         
13360         var cfg = {};
13361         
13362         if(this.inputType != 'hidden'){
13363             cfg.cls = 'form-group' //input-group
13364         }
13365         
13366         var input =  {
13367             tag: 'textarea',
13368             id : id,
13369             warp : this.warp,
13370             rows : this.rows,
13371             value : this.value || '',
13372             html: this.html || '',
13373             cls : 'form-control',
13374             placeholder : this.placeholder || '' 
13375             
13376         };
13377         
13378         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13379             input.maxLength = this.maxLength;
13380         }
13381         
13382         if(this.resize){
13383             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13384         }
13385         
13386         if(this.cols){
13387             input.cols = this.cols;
13388         }
13389         
13390         if (this.readOnly) {
13391             input.readonly = true;
13392         }
13393         
13394         if (this.name) {
13395             input.name = this.name;
13396         }
13397         
13398         if (this.size) {
13399             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13400         }
13401         
13402         var settings=this;
13403         ['xs','sm','md','lg'].map(function(size){
13404             if (settings[size]) {
13405                 cfg.cls += ' col-' + size + '-' + settings[size];
13406             }
13407         });
13408         
13409         var inputblock = input;
13410         
13411         if(this.hasFeedback && !this.allowBlank){
13412             
13413             var feedback = {
13414                 tag: 'span',
13415                 cls: 'glyphicon form-control-feedback'
13416             };
13417
13418             inputblock = {
13419                 cls : 'has-feedback',
13420                 cn :  [
13421                     input,
13422                     feedback
13423                 ] 
13424             };  
13425         }
13426         
13427         
13428         if (this.before || this.after) {
13429             
13430             inputblock = {
13431                 cls : 'input-group',
13432                 cn :  [] 
13433             };
13434             if (this.before) {
13435                 inputblock.cn.push({
13436                     tag :'span',
13437                     cls : 'input-group-addon',
13438                     html : this.before
13439                 });
13440             }
13441             
13442             inputblock.cn.push(input);
13443             
13444             if(this.hasFeedback && !this.allowBlank){
13445                 inputblock.cls += ' has-feedback';
13446                 inputblock.cn.push(feedback);
13447             }
13448             
13449             if (this.after) {
13450                 inputblock.cn.push({
13451                     tag :'span',
13452                     cls : 'input-group-addon',
13453                     html : this.after
13454                 });
13455             }
13456             
13457         }
13458         
13459         if (align ==='left' && this.fieldLabel.length) {
13460             cfg.cn = [
13461                 {
13462                     tag: 'label',
13463                     'for' :  id,
13464                     cls : 'control-label',
13465                     html : this.fieldLabel
13466                 },
13467                 {
13468                     cls : "",
13469                     cn: [
13470                         inputblock
13471                     ]
13472                 }
13473
13474             ];
13475             
13476             if(this.labelWidth > 12){
13477                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13478             }
13479
13480             if(this.labelWidth < 13 && this.labelmd == 0){
13481                 this.labelmd = this.labelWidth;
13482             }
13483
13484             if(this.labellg > 0){
13485                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13486                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13487             }
13488
13489             if(this.labelmd > 0){
13490                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13491                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13492             }
13493
13494             if(this.labelsm > 0){
13495                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13496                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13497             }
13498
13499             if(this.labelxs > 0){
13500                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13501                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13502             }
13503             
13504         } else if ( this.fieldLabel.length) {
13505             cfg.cn = [
13506
13507                {
13508                    tag: 'label',
13509                    //cls : 'input-group-addon',
13510                    html : this.fieldLabel
13511
13512                },
13513
13514                inputblock
13515
13516            ];
13517
13518         } else {
13519
13520             cfg.cn = [
13521
13522                 inputblock
13523
13524             ];
13525                 
13526         }
13527         
13528         if (this.disabled) {
13529             input.disabled=true;
13530         }
13531         
13532         return cfg;
13533         
13534     },
13535     /**
13536      * return the real textarea element.
13537      */
13538     inputEl: function ()
13539     {
13540         return this.el.select('textarea.form-control',true).first();
13541     },
13542     
13543     /**
13544      * Clear any invalid styles/messages for this field
13545      */
13546     clearInvalid : function()
13547     {
13548         
13549         if(!this.el || this.preventMark){ // not rendered
13550             return;
13551         }
13552         
13553         var label = this.el.select('label', true).first();
13554         var icon = this.el.select('i.fa-star', true).first();
13555         
13556         if(label && icon){
13557             icon.remove();
13558         }
13559         this.el.removeClass( this.validClass);
13560         this.inputEl().removeClass('is-invalid');
13561          
13562         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13563             
13564             var feedback = this.el.select('.form-control-feedback', true).first();
13565             
13566             if(feedback){
13567                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13568             }
13569             
13570         }
13571         
13572         this.fireEvent('valid', this);
13573     },
13574     
13575      /**
13576      * Mark this field as valid
13577      */
13578     markValid : function()
13579     {
13580         if(!this.el  || this.preventMark){ // not rendered
13581             return;
13582         }
13583         
13584         this.el.removeClass([this.invalidClass, this.validClass]);
13585         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13586         
13587         var feedback = this.el.select('.form-control-feedback', true).first();
13588             
13589         if(feedback){
13590             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13591         }
13592
13593         if(this.disabled || this.allowBlank){
13594             return;
13595         }
13596         
13597         var label = this.el.select('label', true).first();
13598         var icon = this.el.select('i.fa-star', true).first();
13599         
13600         if(label && icon){
13601             icon.remove();
13602         }
13603         if (Roo.bootstrap.version == 3) {
13604             this.el.addClass(this.validClass);
13605         } else {
13606             this.inputEl().addClass('is-valid');
13607         }
13608         
13609         
13610         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13611             
13612             var feedback = this.el.select('.form-control-feedback', true).first();
13613             
13614             if(feedback){
13615                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13616                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13617             }
13618             
13619         }
13620         
13621         this.fireEvent('valid', this);
13622     },
13623     
13624      /**
13625      * Mark this field as invalid
13626      * @param {String} msg The validation message
13627      */
13628     markInvalid : function(msg)
13629     {
13630         if(!this.el  || this.preventMark){ // not rendered
13631             return;
13632         }
13633         
13634         this.el.removeClass([this.invalidClass, this.validClass]);
13635         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13636         
13637         var feedback = this.el.select('.form-control-feedback', true).first();
13638             
13639         if(feedback){
13640             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13641         }
13642
13643         if(this.disabled || this.allowBlank){
13644             return;
13645         }
13646         
13647         var label = this.el.select('label', true).first();
13648         var icon = this.el.select('i.fa-star', true).first();
13649         
13650         if(!this.getValue().length && label && !icon){
13651             this.el.createChild({
13652                 tag : 'i',
13653                 cls : 'text-danger fa fa-lg fa-star',
13654                 tooltip : 'This field is required',
13655                 style : 'margin-right:5px;'
13656             }, label, true);
13657         }
13658         
13659         if (Roo.bootstrap.version == 3) {
13660             this.el.addClass(this.invalidClass);
13661         } else {
13662             this.inputEl().addClass('is-invalid');
13663         }
13664         
13665         // fixme ... this may be depricated need to test..
13666         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13667             
13668             var feedback = this.el.select('.form-control-feedback', true).first();
13669             
13670             if(feedback){
13671                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13672                 
13673                 if(this.getValue().length || this.forceFeedback){
13674                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13675                 }
13676                 
13677             }
13678             
13679         }
13680         
13681         this.fireEvent('invalid', this, msg);
13682     }
13683 });
13684
13685  
13686 /*
13687  * - LGPL
13688  *
13689  * trigger field - base class for combo..
13690  * 
13691  */
13692  
13693 /**
13694  * @class Roo.bootstrap.form.TriggerField
13695  * @extends Roo.bootstrap.form.Input
13696  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13697  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13698  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13699  * for which you can provide a custom implementation.  For example:
13700  * <pre><code>
13701 var trigger = new Roo.bootstrap.form.TriggerField();
13702 trigger.onTriggerClick = myTriggerFn;
13703 trigger.applyTo('my-field');
13704 </code></pre>
13705  *
13706  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13707  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13708  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13709  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13710  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13711
13712  * @constructor
13713  * Create a new TriggerField.
13714  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13715  * to the base TextField)
13716  */
13717 Roo.bootstrap.form.TriggerField = function(config){
13718     this.mimicing = false;
13719     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13720 };
13721
13722 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13723     /**
13724      * @cfg {String} triggerClass A CSS class to apply to the trigger
13725      */
13726      /**
13727      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13728      */
13729     hideTrigger:false,
13730
13731     /**
13732      * @cfg {Boolean} removable (true|false) special filter default false
13733      */
13734     removable : false,
13735     
13736     /** @cfg {Boolean} grow @hide */
13737     /** @cfg {Number} growMin @hide */
13738     /** @cfg {Number} growMax @hide */
13739
13740     /**
13741      * @hide 
13742      * @method
13743      */
13744     autoSize: Roo.emptyFn,
13745     // private
13746     monitorTab : true,
13747     // private
13748     deferHeight : true,
13749
13750     
13751     actionMode : 'wrap',
13752     
13753     caret : false,
13754     
13755     
13756     getAutoCreate : function(){
13757        
13758         var align = this.labelAlign || this.parentLabelAlign();
13759         
13760         var id = Roo.id();
13761         
13762         var cfg = {
13763             cls: 'form-group' //input-group
13764         };
13765         
13766         
13767         var input =  {
13768             tag: 'input',
13769             id : id,
13770             type : this.inputType,
13771             cls : 'form-control',
13772             autocomplete: 'new-password',
13773             placeholder : this.placeholder || '' 
13774             
13775         };
13776         if (this.name) {
13777             input.name = this.name;
13778         }
13779         if (this.size) {
13780             input.cls += ' input-' + this.size;
13781         }
13782         
13783         if (this.disabled) {
13784             input.disabled=true;
13785         }
13786         
13787         var inputblock = input;
13788         
13789         if(this.hasFeedback && !this.allowBlank){
13790             
13791             var feedback = {
13792                 tag: 'span',
13793                 cls: 'glyphicon form-control-feedback'
13794             };
13795             
13796             if(this.removable && !this.editable  ){
13797                 inputblock = {
13798                     cls : 'has-feedback',
13799                     cn :  [
13800                         inputblock,
13801                         {
13802                             tag: 'button',
13803                             html : 'x',
13804                             cls : 'roo-combo-removable-btn close'
13805                         },
13806                         feedback
13807                     ] 
13808                 };
13809             } else {
13810                 inputblock = {
13811                     cls : 'has-feedback',
13812                     cn :  [
13813                         inputblock,
13814                         feedback
13815                     ] 
13816                 };
13817             }
13818
13819         } else {
13820             if(this.removable && !this.editable ){
13821                 inputblock = {
13822                     cls : 'roo-removable',
13823                     cn :  [
13824                         inputblock,
13825                         {
13826                             tag: 'button',
13827                             html : 'x',
13828                             cls : 'roo-combo-removable-btn close'
13829                         }
13830                     ] 
13831                 };
13832             }
13833         }
13834         
13835         if (this.before || this.after) {
13836             
13837             inputblock = {
13838                 cls : 'input-group',
13839                 cn :  [] 
13840             };
13841             if (this.before) {
13842                 inputblock.cn.push({
13843                     tag :'span',
13844                     cls : 'input-group-addon input-group-prepend input-group-text',
13845                     html : this.before
13846                 });
13847             }
13848             
13849             inputblock.cn.push(input);
13850             
13851             if(this.hasFeedback && !this.allowBlank){
13852                 inputblock.cls += ' has-feedback';
13853                 inputblock.cn.push(feedback);
13854             }
13855             
13856             if (this.after) {
13857                 inputblock.cn.push({
13858                     tag :'span',
13859                     cls : 'input-group-addon input-group-append input-group-text',
13860                     html : this.after
13861                 });
13862             }
13863             
13864         };
13865         
13866       
13867         
13868         var ibwrap = inputblock;
13869         
13870         if(this.multiple){
13871             ibwrap = {
13872                 tag: 'ul',
13873                 cls: 'roo-select2-choices',
13874                 cn:[
13875                     {
13876                         tag: 'li',
13877                         cls: 'roo-select2-search-field',
13878                         cn: [
13879
13880                             inputblock
13881                         ]
13882                     }
13883                 ]
13884             };
13885                 
13886         }
13887         
13888         var combobox = {
13889             cls: 'roo-select2-container input-group',
13890             cn: [
13891                  {
13892                     tag: 'input',
13893                     type : 'hidden',
13894                     cls: 'form-hidden-field'
13895                 },
13896                 ibwrap
13897             ]
13898         };
13899         
13900         if(!this.multiple && this.showToggleBtn){
13901             
13902             var caret = {
13903                         tag: 'span',
13904                         cls: 'caret'
13905              };
13906             if (this.caret != false) {
13907                 caret = {
13908                      tag: 'i',
13909                      cls: 'fa fa-' + this.caret
13910                 };
13911                 
13912             }
13913             
13914             combobox.cn.push({
13915                 tag :'span',
13916                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13917                 cn : [
13918                     Roo.bootstrap.version == 3 ? caret : '',
13919                     {
13920                         tag: 'span',
13921                         cls: 'combobox-clear',
13922                         cn  : [
13923                             {
13924                                 tag : 'i',
13925                                 cls: 'icon-remove'
13926                             }
13927                         ]
13928                     }
13929                 ]
13930
13931             })
13932         }
13933         
13934         if(this.multiple){
13935             combobox.cls += ' roo-select2-container-multi';
13936         }
13937          var indicator = {
13938             tag : 'i',
13939             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13940             tooltip : 'This field is required'
13941         };
13942         if (Roo.bootstrap.version == 4) {
13943             indicator = {
13944                 tag : 'i',
13945                 style : 'display:none'
13946             };
13947         }
13948         
13949         
13950         if (align ==='left' && this.fieldLabel.length) {
13951             
13952             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13953
13954             cfg.cn = [
13955                 indicator,
13956                 {
13957                     tag: 'label',
13958                     'for' :  id,
13959                     cls : 'control-label',
13960                     html : this.fieldLabel
13961
13962                 },
13963                 {
13964                     cls : "", 
13965                     cn: [
13966                         combobox
13967                     ]
13968                 }
13969
13970             ];
13971             
13972             var labelCfg = cfg.cn[1];
13973             var contentCfg = cfg.cn[2];
13974             
13975             if(this.indicatorpos == 'right'){
13976                 cfg.cn = [
13977                     {
13978                         tag: 'label',
13979                         'for' :  id,
13980                         cls : 'control-label',
13981                         cn : [
13982                             {
13983                                 tag : 'span',
13984                                 html : this.fieldLabel
13985                             },
13986                             indicator
13987                         ]
13988                     },
13989                     {
13990                         cls : "", 
13991                         cn: [
13992                             combobox
13993                         ]
13994                     }
13995
13996                 ];
13997                 
13998                 labelCfg = cfg.cn[0];
13999                 contentCfg = cfg.cn[1];
14000             }
14001             
14002             if(this.labelWidth > 12){
14003                 labelCfg.style = "width: " + this.labelWidth + 'px';
14004             }
14005             
14006             if(this.labelWidth < 13 && this.labelmd == 0){
14007                 this.labelmd = this.labelWidth;
14008             }
14009             
14010             if(this.labellg > 0){
14011                 labelCfg.cls += ' col-lg-' + this.labellg;
14012                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14013             }
14014             
14015             if(this.labelmd > 0){
14016                 labelCfg.cls += ' col-md-' + this.labelmd;
14017                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14018             }
14019             
14020             if(this.labelsm > 0){
14021                 labelCfg.cls += ' col-sm-' + this.labelsm;
14022                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14023             }
14024             
14025             if(this.labelxs > 0){
14026                 labelCfg.cls += ' col-xs-' + this.labelxs;
14027                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14028             }
14029             
14030         } else if ( this.fieldLabel.length) {
14031 //                Roo.log(" label");
14032             cfg.cn = [
14033                 indicator,
14034                {
14035                    tag: 'label',
14036                    //cls : 'input-group-addon',
14037                    html : this.fieldLabel
14038
14039                },
14040
14041                combobox
14042
14043             ];
14044             
14045             if(this.indicatorpos == 'right'){
14046                 
14047                 cfg.cn = [
14048                     {
14049                        tag: 'label',
14050                        cn : [
14051                            {
14052                                tag : 'span',
14053                                html : this.fieldLabel
14054                            },
14055                            indicator
14056                        ]
14057
14058                     },
14059                     combobox
14060
14061                 ];
14062
14063             }
14064
14065         } else {
14066             
14067 //                Roo.log(" no label && no align");
14068                 cfg = combobox
14069                      
14070                 
14071         }
14072         
14073         var settings=this;
14074         ['xs','sm','md','lg'].map(function(size){
14075             if (settings[size]) {
14076                 cfg.cls += ' col-' + size + '-' + settings[size];
14077             }
14078         });
14079         
14080         return cfg;
14081         
14082     },
14083     
14084     
14085     
14086     // private
14087     onResize : function(w, h){
14088 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14089 //        if(typeof w == 'number'){
14090 //            var x = w - this.trigger.getWidth();
14091 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14092 //            this.trigger.setStyle('left', x+'px');
14093 //        }
14094     },
14095
14096     // private
14097     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14098
14099     // private
14100     getResizeEl : function(){
14101         return this.inputEl();
14102     },
14103
14104     // private
14105     getPositionEl : function(){
14106         return this.inputEl();
14107     },
14108
14109     // private
14110     alignErrorIcon : function(){
14111         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14112     },
14113
14114     // private
14115     initEvents : function(){
14116         
14117         this.createList();
14118         
14119         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14120         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14121         if(!this.multiple && this.showToggleBtn){
14122             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14123             if(this.hideTrigger){
14124                 this.trigger.setDisplayed(false);
14125             }
14126             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14127         }
14128         
14129         if(this.multiple){
14130             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14131         }
14132         
14133         if(this.removable && !this.editable && !this.tickable){
14134             var close = this.closeTriggerEl();
14135             
14136             if(close){
14137                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14138                 close.on('click', this.removeBtnClick, this, close);
14139             }
14140         }
14141         
14142         //this.trigger.addClassOnOver('x-form-trigger-over');
14143         //this.trigger.addClassOnClick('x-form-trigger-click');
14144         
14145         //if(!this.width){
14146         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14147         //}
14148     },
14149     
14150     closeTriggerEl : function()
14151     {
14152         var close = this.el.select('.roo-combo-removable-btn', true).first();
14153         return close ? close : false;
14154     },
14155     
14156     removeBtnClick : function(e, h, el)
14157     {
14158         e.preventDefault();
14159         
14160         if(this.fireEvent("remove", this) !== false){
14161             this.reset();
14162             this.fireEvent("afterremove", this)
14163         }
14164     },
14165     
14166     createList : function()
14167     {
14168         this.list = Roo.get(document.body).createChild({
14169             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14170             cls: 'typeahead typeahead-long dropdown-menu shadow',
14171             style: 'display:none'
14172         });
14173         
14174         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14175         
14176     },
14177
14178     // private
14179     initTrigger : function(){
14180        
14181     },
14182
14183     // private
14184     onDestroy : function(){
14185         if(this.trigger){
14186             this.trigger.removeAllListeners();
14187           //  this.trigger.remove();
14188         }
14189         //if(this.wrap){
14190         //    this.wrap.remove();
14191         //}
14192         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14193     },
14194
14195     // private
14196     onFocus : function(){
14197         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14198         /*
14199         if(!this.mimicing){
14200             this.wrap.addClass('x-trigger-wrap-focus');
14201             this.mimicing = true;
14202             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14203             if(this.monitorTab){
14204                 this.el.on("keydown", this.checkTab, this);
14205             }
14206         }
14207         */
14208     },
14209
14210     // private
14211     checkTab : function(e){
14212         if(e.getKey() == e.TAB){
14213             this.triggerBlur();
14214         }
14215     },
14216
14217     // private
14218     onBlur : function(){
14219         // do nothing
14220     },
14221
14222     // private
14223     mimicBlur : function(e, t){
14224         /*
14225         if(!this.wrap.contains(t) && this.validateBlur()){
14226             this.triggerBlur();
14227         }
14228         */
14229     },
14230
14231     // private
14232     triggerBlur : function(){
14233         this.mimicing = false;
14234         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14235         if(this.monitorTab){
14236             this.el.un("keydown", this.checkTab, this);
14237         }
14238         //this.wrap.removeClass('x-trigger-wrap-focus');
14239         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14240     },
14241
14242     // private
14243     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14244     validateBlur : function(e, t){
14245         return true;
14246     },
14247
14248     // private
14249     onDisable : function(){
14250         this.inputEl().dom.disabled = true;
14251         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14252         //if(this.wrap){
14253         //    this.wrap.addClass('x-item-disabled');
14254         //}
14255     },
14256
14257     // private
14258     onEnable : function(){
14259         this.inputEl().dom.disabled = false;
14260         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14261         //if(this.wrap){
14262         //    this.el.removeClass('x-item-disabled');
14263         //}
14264     },
14265
14266     // private
14267     onShow : function(){
14268         var ae = this.getActionEl();
14269         
14270         if(ae){
14271             ae.dom.style.display = '';
14272             ae.dom.style.visibility = 'visible';
14273         }
14274     },
14275
14276     // private
14277     
14278     onHide : function(){
14279         var ae = this.getActionEl();
14280         ae.dom.style.display = 'none';
14281     },
14282
14283     /**
14284      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14285      * by an implementing function.
14286      * @method
14287      * @param {EventObject} e
14288      */
14289     onTriggerClick : Roo.emptyFn
14290 });
14291  
14292 /*
14293 * Licence: LGPL
14294 */
14295
14296 /**
14297  * @class Roo.bootstrap.form.CardUploader
14298  * @extends Roo.bootstrap.Button
14299  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14300  * @cfg {Number} errorTimeout default 3000
14301  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14302  * @cfg {Array}  html The button text.
14303
14304  *
14305  * @constructor
14306  * Create a new CardUploader
14307  * @param {Object} config The config object
14308  */
14309
14310 Roo.bootstrap.form.CardUploader = function(config){
14311     
14312  
14313     
14314     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14315     
14316     
14317     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14318         return r.data.id
14319      });
14320     
14321      this.addEvents({
14322          // raw events
14323         /**
14324          * @event preview
14325          * When a image is clicked on - and needs to display a slideshow or similar..
14326          * @param {Roo.bootstrap.Card} this
14327          * @param {Object} The image information data 
14328          *
14329          */
14330         'preview' : true,
14331          /**
14332          * @event download
14333          * When a the download link is clicked
14334          * @param {Roo.bootstrap.Card} this
14335          * @param {Object} The image information data  contains 
14336          */
14337         'download' : true
14338         
14339     });
14340 };
14341  
14342 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14343     
14344      
14345     errorTimeout : 3000,
14346      
14347     images : false,
14348    
14349     fileCollection : false,
14350     allowBlank : true,
14351     
14352     getAutoCreate : function()
14353     {
14354         
14355         var cfg =  {
14356             cls :'form-group' ,
14357             cn : [
14358                
14359                 {
14360                     tag: 'label',
14361                    //cls : 'input-group-addon',
14362                     html : this.fieldLabel
14363
14364                 },
14365
14366                 {
14367                     tag: 'input',
14368                     type : 'hidden',
14369                     name : this.name,
14370                     value : this.value,
14371                     cls : 'd-none  form-control'
14372                 },
14373                 
14374                 {
14375                     tag: 'input',
14376                     multiple : 'multiple',
14377                     type : 'file',
14378                     cls : 'd-none  roo-card-upload-selector'
14379                 },
14380                 
14381                 {
14382                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14383                 },
14384                 {
14385                     cls : 'card-columns roo-card-uploader-container'
14386                 }
14387
14388             ]
14389         };
14390            
14391          
14392         return cfg;
14393     },
14394     
14395     getChildContainer : function() /// what children are added to.
14396     {
14397         return this.containerEl;
14398     },
14399    
14400     getButtonContainer : function() /// what children are added to.
14401     {
14402         return this.el.select(".roo-card-uploader-button-container").first();
14403     },
14404    
14405     initEvents : function()
14406     {
14407         
14408         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14409         
14410         var t = this;
14411         this.addxtype({
14412             xns: Roo.bootstrap,
14413
14414             xtype : 'Button',
14415             container_method : 'getButtonContainer' ,            
14416             html :  this.html, // fix changable?
14417             cls : 'w-100 ',
14418             listeners : {
14419                 'click' : function(btn, e) {
14420                     t.onClick(e);
14421                 }
14422             }
14423         });
14424         
14425         
14426         
14427         
14428         this.urlAPI = (window.createObjectURL && window) || 
14429                                 (window.URL && URL.revokeObjectURL && URL) || 
14430                                 (window.webkitURL && webkitURL);
14431                         
14432          
14433          
14434          
14435         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14436         
14437         this.selectorEl.on('change', this.onFileSelected, this);
14438         if (this.images) {
14439             var t = this;
14440             this.images.forEach(function(img) {
14441                 t.addCard(img)
14442             });
14443             this.images = false;
14444         }
14445         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14446          
14447        
14448     },
14449     
14450    
14451     onClick : function(e)
14452     {
14453         e.preventDefault();
14454          
14455         this.selectorEl.dom.click();
14456          
14457     },
14458     
14459     onFileSelected : function(e)
14460     {
14461         e.preventDefault();
14462         
14463         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14464             return;
14465         }
14466         
14467         Roo.each(this.selectorEl.dom.files, function(file){    
14468             this.addFile(file);
14469         }, this);
14470          
14471     },
14472     
14473       
14474     
14475       
14476     
14477     addFile : function(file)
14478     {
14479            
14480         if(typeof(file) === 'string'){
14481             throw "Add file by name?"; // should not happen
14482             return;
14483         }
14484         
14485         if(!file || !this.urlAPI){
14486             return;
14487         }
14488         
14489         // file;
14490         // file.type;
14491         
14492         var _this = this;
14493         
14494         
14495         var url = _this.urlAPI.createObjectURL( file);
14496            
14497         this.addCard({
14498             id : Roo.bootstrap.form.CardUploader.ID--,
14499             is_uploaded : false,
14500             src : url,
14501             srcfile : file,
14502             title : file.name,
14503             mimetype : file.type,
14504             preview : false,
14505             is_deleted : 0
14506         });
14507         
14508     },
14509     
14510     /**
14511      * addCard - add an Attachment to the uploader
14512      * @param data - the data about the image to upload
14513      *
14514      * {
14515           id : 123
14516           title : "Title of file",
14517           is_uploaded : false,
14518           src : "http://.....",
14519           srcfile : { the File upload object },
14520           mimetype : file.type,
14521           preview : false,
14522           is_deleted : 0
14523           .. any other data...
14524         }
14525      *
14526      * 
14527     */
14528     
14529     addCard : function (data)
14530     {
14531         // hidden input element?
14532         // if the file is not an image...
14533         //then we need to use something other that and header_image
14534         var t = this;
14535         //   remove.....
14536         var footer = [
14537             {
14538                 xns : Roo.bootstrap,
14539                 xtype : 'CardFooter',
14540                  items: [
14541                     {
14542                         xns : Roo.bootstrap,
14543                         xtype : 'Element',
14544                         cls : 'd-flex',
14545                         items : [
14546                             
14547                             {
14548                                 xns : Roo.bootstrap,
14549                                 xtype : 'Button',
14550                                 html : String.format("<small>{0}</small>", data.title),
14551                                 cls : 'col-10 text-left',
14552                                 size: 'sm',
14553                                 weight: 'link',
14554                                 fa : 'download',
14555                                 listeners : {
14556                                     click : function() {
14557                                      
14558                                         t.fireEvent( "download", t, data );
14559                                     }
14560                                 }
14561                             },
14562                           
14563                             {
14564                                 xns : Roo.bootstrap,
14565                                 xtype : 'Button',
14566                                 style: 'max-height: 28px; ',
14567                                 size : 'sm',
14568                                 weight: 'danger',
14569                                 cls : 'col-2',
14570                                 fa : 'times',
14571                                 listeners : {
14572                                     click : function() {
14573                                         t.removeCard(data.id)
14574                                     }
14575                                 }
14576                             }
14577                         ]
14578                     }
14579                     
14580                 ] 
14581             }
14582             
14583         ];
14584         
14585         var cn = this.addxtype(
14586             {
14587                  
14588                 xns : Roo.bootstrap,
14589                 xtype : 'Card',
14590                 closeable : true,
14591                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14592                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14593                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14594                 data : data,
14595                 html : false,
14596                  
14597                 items : footer,
14598                 initEvents : function() {
14599                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14600                     var card = this;
14601                     this.imgEl = this.el.select('.card-img-top').first();
14602                     if (this.imgEl) {
14603                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14604                         this.imgEl.set({ 'pointer' : 'cursor' });
14605                                   
14606                     }
14607                     this.getCardFooter().addClass('p-1');
14608                     
14609                   
14610                 }
14611                 
14612             }
14613         );
14614         // dont' really need ot update items.
14615         // this.items.push(cn);
14616         this.fileCollection.add(cn);
14617         
14618         if (!data.srcfile) {
14619             this.updateInput();
14620             return;
14621         }
14622             
14623         var _t = this;
14624         var reader = new FileReader();
14625         reader.addEventListener("load", function() {  
14626             data.srcdata =  reader.result;
14627             _t.updateInput();
14628         });
14629         reader.readAsDataURL(data.srcfile);
14630         
14631         
14632         
14633     },
14634     removeCard : function(id)
14635     {
14636         
14637         var card  = this.fileCollection.get(id);
14638         card.data.is_deleted = 1;
14639         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14640         //this.fileCollection.remove(card);
14641         //this.items = this.items.filter(function(e) { return e != card });
14642         // dont' really need ot update items.
14643         card.el.dom.parentNode.removeChild(card.el.dom);
14644         this.updateInput();
14645
14646         
14647     },
14648     reset: function()
14649     {
14650         this.fileCollection.each(function(card) {
14651             if (card.el.dom && card.el.dom.parentNode) {
14652                 card.el.dom.parentNode.removeChild(card.el.dom);
14653             }
14654         });
14655         this.fileCollection.clear();
14656         this.updateInput();
14657     },
14658     
14659     updateInput : function()
14660     {
14661          var data = [];
14662         this.fileCollection.each(function(e) {
14663             data.push(e.data);
14664             
14665         });
14666         this.inputEl().dom.value = JSON.stringify(data);
14667         
14668         
14669         
14670     }
14671     
14672     
14673 });
14674
14675
14676 Roo.bootstrap.form.CardUploader.ID = -1;/*
14677  * Based on:
14678  * Ext JS Library 1.1.1
14679  * Copyright(c) 2006-2007, Ext JS, LLC.
14680  *
14681  * Originally Released Under LGPL - original licence link has changed is not relivant.
14682  *
14683  * Fork - LGPL
14684  * <script type="text/javascript">
14685  */
14686
14687
14688 /**
14689  * @class Roo.data.SortTypes
14690  * @static
14691  * Defines the default sorting (casting?) comparison functions used when sorting data.
14692  */
14693 Roo.data.SortTypes = {
14694     /**
14695      * Default sort that does nothing
14696      * @param {Mixed} s The value being converted
14697      * @return {Mixed} The comparison value
14698      */
14699     none : function(s){
14700         return s;
14701     },
14702     
14703     /**
14704      * The regular expression used to strip tags
14705      * @type {RegExp}
14706      * @property
14707      */
14708     stripTagsRE : /<\/?[^>]+>/gi,
14709     
14710     /**
14711      * Strips all HTML tags to sort on text only
14712      * @param {Mixed} s The value being converted
14713      * @return {String} The comparison value
14714      */
14715     asText : function(s){
14716         return String(s).replace(this.stripTagsRE, "");
14717     },
14718     
14719     /**
14720      * Strips all HTML tags to sort on text only - Case insensitive
14721      * @param {Mixed} s The value being converted
14722      * @return {String} The comparison value
14723      */
14724     asUCText : function(s){
14725         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14726     },
14727     
14728     /**
14729      * Case insensitive string
14730      * @param {Mixed} s The value being converted
14731      * @return {String} The comparison value
14732      */
14733     asUCString : function(s) {
14734         return String(s).toUpperCase();
14735     },
14736     
14737     /**
14738      * Date sorting
14739      * @param {Mixed} s The value being converted
14740      * @return {Number} The comparison value
14741      */
14742     asDate : function(s) {
14743         if(!s){
14744             return 0;
14745         }
14746         if(s instanceof Date){
14747             return s.getTime();
14748         }
14749         return Date.parse(String(s));
14750     },
14751     
14752     /**
14753      * Float sorting
14754      * @param {Mixed} s The value being converted
14755      * @return {Float} The comparison value
14756      */
14757     asFloat : function(s) {
14758         var val = parseFloat(String(s).replace(/,/g, ""));
14759         if(isNaN(val)) {
14760             val = 0;
14761         }
14762         return val;
14763     },
14764     
14765     /**
14766      * Integer sorting
14767      * @param {Mixed} s The value being converted
14768      * @return {Number} The comparison value
14769      */
14770     asInt : function(s) {
14771         var val = parseInt(String(s).replace(/,/g, ""));
14772         if(isNaN(val)) {
14773             val = 0;
14774         }
14775         return val;
14776     }
14777 };/*
14778  * Based on:
14779  * Ext JS Library 1.1.1
14780  * Copyright(c) 2006-2007, Ext JS, LLC.
14781  *
14782  * Originally Released Under LGPL - original licence link has changed is not relivant.
14783  *
14784  * Fork - LGPL
14785  * <script type="text/javascript">
14786  */
14787
14788 /**
14789 * @class Roo.data.Record
14790  * Instances of this class encapsulate both record <em>definition</em> information, and record
14791  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14792  * to access Records cached in an {@link Roo.data.Store} object.<br>
14793  * <p>
14794  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14795  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14796  * objects.<br>
14797  * <p>
14798  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14799  * @constructor
14800  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14801  * {@link #create}. The parameters are the same.
14802  * @param {Array} data An associative Array of data values keyed by the field name.
14803  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14804  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14805  * not specified an integer id is generated.
14806  */
14807 Roo.data.Record = function(data, id){
14808     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14809     this.data = data;
14810 };
14811
14812 /**
14813  * Generate a constructor for a specific record layout.
14814  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14815  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14816  * Each field definition object may contain the following properties: <ul>
14817  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14818  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14819  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14820  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14821  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14822  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14823  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14824  * this may be omitted.</p></li>
14825  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14826  * <ul><li>auto (Default, implies no conversion)</li>
14827  * <li>string</li>
14828  * <li>int</li>
14829  * <li>float</li>
14830  * <li>boolean</li>
14831  * <li>date</li></ul></p></li>
14832  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14833  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14834  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14835  * by the Reader into an object that will be stored in the Record. It is passed the
14836  * following parameters:<ul>
14837  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14838  * </ul></p></li>
14839  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14840  * </ul>
14841  * <br>usage:<br><pre><code>
14842 var TopicRecord = Roo.data.Record.create(
14843     {name: 'title', mapping: 'topic_title'},
14844     {name: 'author', mapping: 'username'},
14845     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14846     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14847     {name: 'lastPoster', mapping: 'user2'},
14848     {name: 'excerpt', mapping: 'post_text'}
14849 );
14850
14851 var myNewRecord = new TopicRecord({
14852     title: 'Do my job please',
14853     author: 'noobie',
14854     totalPosts: 1,
14855     lastPost: new Date(),
14856     lastPoster: 'Animal',
14857     excerpt: 'No way dude!'
14858 });
14859 myStore.add(myNewRecord);
14860 </code></pre>
14861  * @method create
14862  * @static
14863  */
14864 Roo.data.Record.create = function(o){
14865     var f = function(){
14866         f.superclass.constructor.apply(this, arguments);
14867     };
14868     Roo.extend(f, Roo.data.Record);
14869     var p = f.prototype;
14870     p.fields = new Roo.util.MixedCollection(false, function(field){
14871         return field.name;
14872     });
14873     for(var i = 0, len = o.length; i < len; i++){
14874         p.fields.add(new Roo.data.Field(o[i]));
14875     }
14876     f.getField = function(name){
14877         return p.fields.get(name);  
14878     };
14879     return f;
14880 };
14881
14882 Roo.data.Record.AUTO_ID = 1000;
14883 Roo.data.Record.EDIT = 'edit';
14884 Roo.data.Record.REJECT = 'reject';
14885 Roo.data.Record.COMMIT = 'commit';
14886
14887 Roo.data.Record.prototype = {
14888     /**
14889      * Readonly flag - true if this record has been modified.
14890      * @type Boolean
14891      */
14892     dirty : false,
14893     editing : false,
14894     error: null,
14895     modified: null,
14896
14897     // private
14898     join : function(store){
14899         this.store = store;
14900     },
14901
14902     /**
14903      * Set the named field to the specified value.
14904      * @param {String} name The name of the field to set.
14905      * @param {Object} value The value to set the field to.
14906      */
14907     set : function(name, value){
14908         if(this.data[name] == value){
14909             return;
14910         }
14911         this.dirty = true;
14912         if(!this.modified){
14913             this.modified = {};
14914         }
14915         if(typeof this.modified[name] == 'undefined'){
14916             this.modified[name] = this.data[name];
14917         }
14918         this.data[name] = value;
14919         if(!this.editing && this.store){
14920             this.store.afterEdit(this);
14921         }       
14922     },
14923
14924     /**
14925      * Get the value of the named field.
14926      * @param {String} name The name of the field to get the value of.
14927      * @return {Object} The value of the field.
14928      */
14929     get : function(name){
14930         return this.data[name]; 
14931     },
14932
14933     // private
14934     beginEdit : function(){
14935         this.editing = true;
14936         this.modified = {}; 
14937     },
14938
14939     // private
14940     cancelEdit : function(){
14941         this.editing = false;
14942         delete this.modified;
14943     },
14944
14945     // private
14946     endEdit : function(){
14947         this.editing = false;
14948         if(this.dirty && this.store){
14949             this.store.afterEdit(this);
14950         }
14951     },
14952
14953     /**
14954      * Usually called by the {@link Roo.data.Store} which owns the Record.
14955      * Rejects all changes made to the Record since either creation, or the last commit operation.
14956      * Modified fields are reverted to their original values.
14957      * <p>
14958      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14959      * of reject operations.
14960      */
14961     reject : function(){
14962         var m = this.modified;
14963         for(var n in m){
14964             if(typeof m[n] != "function"){
14965                 this.data[n] = m[n];
14966             }
14967         }
14968         this.dirty = false;
14969         delete this.modified;
14970         this.editing = false;
14971         if(this.store){
14972             this.store.afterReject(this);
14973         }
14974     },
14975
14976     /**
14977      * Usually called by the {@link Roo.data.Store} which owns the Record.
14978      * Commits all changes made to the Record since either creation, or the last commit operation.
14979      * <p>
14980      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14981      * of commit operations.
14982      */
14983     commit : function(){
14984         this.dirty = false;
14985         delete this.modified;
14986         this.editing = false;
14987         if(this.store){
14988             this.store.afterCommit(this);
14989         }
14990     },
14991
14992     // private
14993     hasError : function(){
14994         return this.error != null;
14995     },
14996
14997     // private
14998     clearError : function(){
14999         this.error = null;
15000     },
15001
15002     /**
15003      * Creates a copy of this record.
15004      * @param {String} id (optional) A new record id if you don't want to use this record's id
15005      * @return {Record}
15006      */
15007     copy : function(newId) {
15008         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15009     }
15010 };/*
15011  * Based on:
15012  * Ext JS Library 1.1.1
15013  * Copyright(c) 2006-2007, Ext JS, LLC.
15014  *
15015  * Originally Released Under LGPL - original licence link has changed is not relivant.
15016  *
15017  * Fork - LGPL
15018  * <script type="text/javascript">
15019  */
15020
15021
15022
15023 /**
15024  * @class Roo.data.Store
15025  * @extends Roo.util.Observable
15026  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15027  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15028  * <p>
15029  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15030  * has no knowledge of the format of the data returned by the Proxy.<br>
15031  * <p>
15032  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15033  * instances from the data object. These records are cached and made available through accessor functions.
15034  * @constructor
15035  * Creates a new Store.
15036  * @param {Object} config A config object containing the objects needed for the Store to access data,
15037  * and read the data into Records.
15038  */
15039 Roo.data.Store = function(config){
15040     this.data = new Roo.util.MixedCollection(false);
15041     this.data.getKey = function(o){
15042         return o.id;
15043     };
15044     this.baseParams = {};
15045     // private
15046     this.paramNames = {
15047         "start" : "start",
15048         "limit" : "limit",
15049         "sort" : "sort",
15050         "dir" : "dir",
15051         "multisort" : "_multisort"
15052     };
15053
15054     if(config && config.data){
15055         this.inlineData = config.data;
15056         delete config.data;
15057     }
15058
15059     Roo.apply(this, config);
15060     
15061     if(this.reader){ // reader passed
15062         this.reader = Roo.factory(this.reader, Roo.data);
15063         this.reader.xmodule = this.xmodule || false;
15064         if(!this.recordType){
15065             this.recordType = this.reader.recordType;
15066         }
15067         if(this.reader.onMetaChange){
15068             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15069         }
15070     }
15071
15072     if(this.recordType){
15073         this.fields = this.recordType.prototype.fields;
15074     }
15075     this.modified = [];
15076
15077     this.addEvents({
15078         /**
15079          * @event datachanged
15080          * Fires when the data cache has changed, and a widget which is using this Store
15081          * as a Record cache should refresh its view.
15082          * @param {Store} this
15083          */
15084         datachanged : true,
15085         /**
15086          * @event metachange
15087          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15088          * @param {Store} this
15089          * @param {Object} meta The JSON metadata
15090          */
15091         metachange : true,
15092         /**
15093          * @event add
15094          * Fires when Records have been added to the Store
15095          * @param {Store} this
15096          * @param {Roo.data.Record[]} records The array of Records added
15097          * @param {Number} index The index at which the record(s) were added
15098          */
15099         add : true,
15100         /**
15101          * @event remove
15102          * Fires when a Record has been removed from the Store
15103          * @param {Store} this
15104          * @param {Roo.data.Record} record The Record that was removed
15105          * @param {Number} index The index at which the record was removed
15106          */
15107         remove : true,
15108         /**
15109          * @event update
15110          * Fires when a Record has been updated
15111          * @param {Store} this
15112          * @param {Roo.data.Record} record The Record that was updated
15113          * @param {String} operation The update operation being performed.  Value may be one of:
15114          * <pre><code>
15115  Roo.data.Record.EDIT
15116  Roo.data.Record.REJECT
15117  Roo.data.Record.COMMIT
15118          * </code></pre>
15119          */
15120         update : true,
15121         /**
15122          * @event clear
15123          * Fires when the data cache has been cleared.
15124          * @param {Store} this
15125          */
15126         clear : true,
15127         /**
15128          * @event beforeload
15129          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15130          * the load action will be canceled.
15131          * @param {Store} this
15132          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15133          */
15134         beforeload : true,
15135         /**
15136          * @event beforeloadadd
15137          * Fires after a new set of Records has been loaded.
15138          * @param {Store} this
15139          * @param {Roo.data.Record[]} records The Records that were loaded
15140          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15141          */
15142         beforeloadadd : true,
15143         /**
15144          * @event load
15145          * Fires after a new set of Records has been loaded, before they are added to the store.
15146          * @param {Store} this
15147          * @param {Roo.data.Record[]} records The Records that were loaded
15148          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15149          * @params {Object} return from reader
15150          */
15151         load : true,
15152         /**
15153          * @event loadexception
15154          * Fires if an exception occurs in the Proxy during loading.
15155          * Called with the signature of the Proxy's "loadexception" event.
15156          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15157          * 
15158          * @param {Proxy} 
15159          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15160          * @param {Object} load options 
15161          * @param {Object} jsonData from your request (normally this contains the Exception)
15162          */
15163         loadexception : true
15164     });
15165     
15166     if(this.proxy){
15167         this.proxy = Roo.factory(this.proxy, Roo.data);
15168         this.proxy.xmodule = this.xmodule || false;
15169         this.relayEvents(this.proxy,  ["loadexception"]);
15170     }
15171     this.sortToggle = {};
15172     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15173
15174     Roo.data.Store.superclass.constructor.call(this);
15175
15176     if(this.inlineData){
15177         this.loadData(this.inlineData);
15178         delete this.inlineData;
15179     }
15180 };
15181
15182 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15183      /**
15184     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15185     * without a remote query - used by combo/forms at present.
15186     */
15187     
15188     /**
15189     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15190     */
15191     /**
15192     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15193     */
15194     /**
15195     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15196     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15197     */
15198     /**
15199     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15200     * on any HTTP request
15201     */
15202     /**
15203     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15204     */
15205     /**
15206     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15207     */
15208     multiSort: false,
15209     /**
15210     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15211     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15212     */
15213     remoteSort : false,
15214
15215     /**
15216     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15217      * loaded or when a record is removed. (defaults to false).
15218     */
15219     pruneModifiedRecords : false,
15220
15221     // private
15222     lastOptions : null,
15223
15224     /**
15225      * Add Records to the Store and fires the add event.
15226      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15227      */
15228     add : function(records){
15229         records = [].concat(records);
15230         for(var i = 0, len = records.length; i < len; i++){
15231             records[i].join(this);
15232         }
15233         var index = this.data.length;
15234         this.data.addAll(records);
15235         this.fireEvent("add", this, records, index);
15236     },
15237
15238     /**
15239      * Remove a Record from the Store and fires the remove event.
15240      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15241      */
15242     remove : function(record){
15243         var index = this.data.indexOf(record);
15244         this.data.removeAt(index);
15245  
15246         if(this.pruneModifiedRecords){
15247             this.modified.remove(record);
15248         }
15249         this.fireEvent("remove", this, record, index);
15250     },
15251
15252     /**
15253      * Remove all Records from the Store and fires the clear event.
15254      */
15255     removeAll : function(){
15256         this.data.clear();
15257         if(this.pruneModifiedRecords){
15258             this.modified = [];
15259         }
15260         this.fireEvent("clear", this);
15261     },
15262
15263     /**
15264      * Inserts Records to the Store at the given index and fires the add event.
15265      * @param {Number} index The start index at which to insert the passed Records.
15266      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15267      */
15268     insert : function(index, records){
15269         records = [].concat(records);
15270         for(var i = 0, len = records.length; i < len; i++){
15271             this.data.insert(index, records[i]);
15272             records[i].join(this);
15273         }
15274         this.fireEvent("add", this, records, index);
15275     },
15276
15277     /**
15278      * Get the index within the cache of the passed Record.
15279      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15280      * @return {Number} The index of the passed Record. Returns -1 if not found.
15281      */
15282     indexOf : function(record){
15283         return this.data.indexOf(record);
15284     },
15285
15286     /**
15287      * Get the index within the cache of the Record with the passed id.
15288      * @param {String} id The id of the Record to find.
15289      * @return {Number} The index of the Record. Returns -1 if not found.
15290      */
15291     indexOfId : function(id){
15292         return this.data.indexOfKey(id);
15293     },
15294
15295     /**
15296      * Get the Record with the specified id.
15297      * @param {String} id The id of the Record to find.
15298      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15299      */
15300     getById : function(id){
15301         return this.data.key(id);
15302     },
15303
15304     /**
15305      * Get the Record at the specified index.
15306      * @param {Number} index The index of the Record to find.
15307      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15308      */
15309     getAt : function(index){
15310         return this.data.itemAt(index);
15311     },
15312
15313     /**
15314      * Returns a range of Records between specified indices.
15315      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15316      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15317      * @return {Roo.data.Record[]} An array of Records
15318      */
15319     getRange : function(start, end){
15320         return this.data.getRange(start, end);
15321     },
15322
15323     // private
15324     storeOptions : function(o){
15325         o = Roo.apply({}, o);
15326         delete o.callback;
15327         delete o.scope;
15328         this.lastOptions = o;
15329     },
15330
15331     /**
15332      * Loads the Record cache from the configured Proxy using the configured Reader.
15333      * <p>
15334      * If using remote paging, then the first load call must specify the <em>start</em>
15335      * and <em>limit</em> properties in the options.params property to establish the initial
15336      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15337      * <p>
15338      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15339      * and this call will return before the new data has been loaded. Perform any post-processing
15340      * in a callback function, or in a "load" event handler.</strong>
15341      * <p>
15342      * @param {Object} options An object containing properties which control loading options:<ul>
15343      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15344      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15345      * passed the following arguments:<ul>
15346      * <li>r : Roo.data.Record[]</li>
15347      * <li>options: Options object from the load call</li>
15348      * <li>success: Boolean success indicator</li></ul></li>
15349      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15350      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15351      * </ul>
15352      */
15353     load : function(options){
15354         options = options || {};
15355         if(this.fireEvent("beforeload", this, options) !== false){
15356             this.storeOptions(options);
15357             var p = Roo.apply(options.params || {}, this.baseParams);
15358             // if meta was not loaded from remote source.. try requesting it.
15359             if (!this.reader.metaFromRemote) {
15360                 p._requestMeta = 1;
15361             }
15362             if(this.sortInfo && this.remoteSort){
15363                 var pn = this.paramNames;
15364                 p[pn["sort"]] = this.sortInfo.field;
15365                 p[pn["dir"]] = this.sortInfo.direction;
15366             }
15367             if (this.multiSort) {
15368                 var pn = this.paramNames;
15369                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15370             }
15371             
15372             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15373         }
15374     },
15375
15376     /**
15377      * Reloads the Record cache from the configured Proxy using the configured Reader and
15378      * the options from the last load operation performed.
15379      * @param {Object} options (optional) An object containing properties which may override the options
15380      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15381      * the most recently used options are reused).
15382      */
15383     reload : function(options){
15384         this.load(Roo.applyIf(options||{}, this.lastOptions));
15385     },
15386
15387     // private
15388     // Called as a callback by the Reader during a load operation.
15389     loadRecords : function(o, options, success){
15390         if(!o || success === false){
15391             if(success !== false){
15392                 this.fireEvent("load", this, [], options, o);
15393             }
15394             if(options.callback){
15395                 options.callback.call(options.scope || this, [], options, false);
15396             }
15397             return;
15398         }
15399         // if data returned failure - throw an exception.
15400         if (o.success === false) {
15401             // show a message if no listener is registered.
15402             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15403                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15404             }
15405             // loadmask wil be hooked into this..
15406             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15407             return;
15408         }
15409         var r = o.records, t = o.totalRecords || r.length;
15410         
15411         this.fireEvent("beforeloadadd", this, r, options, o);
15412         
15413         if(!options || options.add !== true){
15414             if(this.pruneModifiedRecords){
15415                 this.modified = [];
15416             }
15417             for(var i = 0, len = r.length; i < len; i++){
15418                 r[i].join(this);
15419             }
15420             if(this.snapshot){
15421                 this.data = this.snapshot;
15422                 delete this.snapshot;
15423             }
15424             this.data.clear();
15425             this.data.addAll(r);
15426             this.totalLength = t;
15427             this.applySort();
15428             this.fireEvent("datachanged", this);
15429         }else{
15430             this.totalLength = Math.max(t, this.data.length+r.length);
15431             this.add(r);
15432         }
15433         
15434         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15435                 
15436             var e = new Roo.data.Record({});
15437
15438             e.set(this.parent.displayField, this.parent.emptyTitle);
15439             e.set(this.parent.valueField, '');
15440
15441             this.insert(0, e);
15442         }
15443             
15444         this.fireEvent("load", this, r, options, o);
15445         if(options.callback){
15446             options.callback.call(options.scope || this, r, options, true);
15447         }
15448     },
15449
15450
15451     /**
15452      * Loads data from a passed data block. A Reader which understands the format of the data
15453      * must have been configured in the constructor.
15454      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15455      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15456      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15457      */
15458     loadData : function(o, append){
15459         var r = this.reader.readRecords(o);
15460         this.loadRecords(r, {add: append}, true);
15461     },
15462     
15463      /**
15464      * using 'cn' the nested child reader read the child array into it's child stores.
15465      * @param {Object} rec The record with a 'children array
15466      */
15467     loadDataFromChildren : function(rec)
15468     {
15469         this.loadData(this.reader.toLoadData(rec));
15470     },
15471     
15472
15473     /**
15474      * Gets the number of cached records.
15475      * <p>
15476      * <em>If using paging, this may not be the total size of the dataset. If the data object
15477      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15478      * the data set size</em>
15479      */
15480     getCount : function(){
15481         return this.data.length || 0;
15482     },
15483
15484     /**
15485      * Gets the total number of records in the dataset as returned by the server.
15486      * <p>
15487      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15488      * the dataset size</em>
15489      */
15490     getTotalCount : function(){
15491         return this.totalLength || 0;
15492     },
15493
15494     /**
15495      * Returns the sort state of the Store as an object with two properties:
15496      * <pre><code>
15497  field {String} The name of the field by which the Records are sorted
15498  direction {String} The sort order, "ASC" or "DESC"
15499      * </code></pre>
15500      */
15501     getSortState : function(){
15502         return this.sortInfo;
15503     },
15504
15505     // private
15506     applySort : function(){
15507         if(this.sortInfo && !this.remoteSort){
15508             var s = this.sortInfo, f = s.field;
15509             var st = this.fields.get(f).sortType;
15510             var fn = function(r1, r2){
15511                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15512                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15513             };
15514             this.data.sort(s.direction, fn);
15515             if(this.snapshot && this.snapshot != this.data){
15516                 this.snapshot.sort(s.direction, fn);
15517             }
15518         }
15519     },
15520
15521     /**
15522      * Sets the default sort column and order to be used by the next load operation.
15523      * @param {String} fieldName The name of the field to sort by.
15524      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15525      */
15526     setDefaultSort : function(field, dir){
15527         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15528     },
15529
15530     /**
15531      * Sort the Records.
15532      * If remote sorting is used, the sort is performed on the server, and the cache is
15533      * reloaded. If local sorting is used, the cache is sorted internally.
15534      * @param {String} fieldName The name of the field to sort by.
15535      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15536      */
15537     sort : function(fieldName, dir){
15538         var f = this.fields.get(fieldName);
15539         if(!dir){
15540             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15541             
15542             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15543                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15544             }else{
15545                 dir = f.sortDir;
15546             }
15547         }
15548         this.sortToggle[f.name] = dir;
15549         this.sortInfo = {field: f.name, direction: dir};
15550         if(!this.remoteSort){
15551             this.applySort();
15552             this.fireEvent("datachanged", this);
15553         }else{
15554             this.load(this.lastOptions);
15555         }
15556     },
15557
15558     /**
15559      * Calls the specified function for each of the Records in the cache.
15560      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15561      * Returning <em>false</em> aborts and exits the iteration.
15562      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15563      */
15564     each : function(fn, scope){
15565         this.data.each(fn, scope);
15566     },
15567
15568     /**
15569      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15570      * (e.g., during paging).
15571      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15572      */
15573     getModifiedRecords : function(){
15574         return this.modified;
15575     },
15576
15577     // private
15578     createFilterFn : function(property, value, anyMatch){
15579         if(!value.exec){ // not a regex
15580             value = String(value);
15581             if(value.length == 0){
15582                 return false;
15583             }
15584             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15585         }
15586         return function(r){
15587             return value.test(r.data[property]);
15588         };
15589     },
15590
15591     /**
15592      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15593      * @param {String} property A field on your records
15594      * @param {Number} start The record index to start at (defaults to 0)
15595      * @param {Number} end The last record index to include (defaults to length - 1)
15596      * @return {Number} The sum
15597      */
15598     sum : function(property, start, end){
15599         var rs = this.data.items, v = 0;
15600         start = start || 0;
15601         end = (end || end === 0) ? end : rs.length-1;
15602
15603         for(var i = start; i <= end; i++){
15604             v += (rs[i].data[property] || 0);
15605         }
15606         return v;
15607     },
15608
15609     /**
15610      * Filter the records by a specified property.
15611      * @param {String} field A field on your records
15612      * @param {String/RegExp} value Either a string that the field
15613      * should start with or a RegExp to test against the field
15614      * @param {Boolean} anyMatch True to match any part not just the beginning
15615      */
15616     filter : function(property, value, anyMatch){
15617         var fn = this.createFilterFn(property, value, anyMatch);
15618         return fn ? this.filterBy(fn) : this.clearFilter();
15619     },
15620
15621     /**
15622      * Filter by a function. The specified function will be called with each
15623      * record in this data source. If the function returns true the record is included,
15624      * otherwise it is filtered.
15625      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15626      * @param {Object} scope (optional) The scope of the function (defaults to this)
15627      */
15628     filterBy : function(fn, scope){
15629         this.snapshot = this.snapshot || this.data;
15630         this.data = this.queryBy(fn, scope||this);
15631         this.fireEvent("datachanged", this);
15632     },
15633
15634     /**
15635      * Query the records by a specified property.
15636      * @param {String} field A field on your records
15637      * @param {String/RegExp} value Either a string that the field
15638      * should start with or a RegExp to test against the field
15639      * @param {Boolean} anyMatch True to match any part not just the beginning
15640      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15641      */
15642     query : function(property, value, anyMatch){
15643         var fn = this.createFilterFn(property, value, anyMatch);
15644         return fn ? this.queryBy(fn) : this.data.clone();
15645     },
15646
15647     /**
15648      * Query by a function. The specified function will be called with each
15649      * record in this data source. If the function returns true the record is included
15650      * in the results.
15651      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15652      * @param {Object} scope (optional) The scope of the function (defaults to this)
15653       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15654      **/
15655     queryBy : function(fn, scope){
15656         var data = this.snapshot || this.data;
15657         return data.filterBy(fn, scope||this);
15658     },
15659
15660     /**
15661      * Collects unique values for a particular dataIndex from this store.
15662      * @param {String} dataIndex The property to collect
15663      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15664      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15665      * @return {Array} An array of the unique values
15666      **/
15667     collect : function(dataIndex, allowNull, bypassFilter){
15668         var d = (bypassFilter === true && this.snapshot) ?
15669                 this.snapshot.items : this.data.items;
15670         var v, sv, r = [], l = {};
15671         for(var i = 0, len = d.length; i < len; i++){
15672             v = d[i].data[dataIndex];
15673             sv = String(v);
15674             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15675                 l[sv] = true;
15676                 r[r.length] = v;
15677             }
15678         }
15679         return r;
15680     },
15681
15682     /**
15683      * Revert to a view of the Record cache with no filtering applied.
15684      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15685      */
15686     clearFilter : function(suppressEvent){
15687         if(this.snapshot && this.snapshot != this.data){
15688             this.data = this.snapshot;
15689             delete this.snapshot;
15690             if(suppressEvent !== true){
15691                 this.fireEvent("datachanged", this);
15692             }
15693         }
15694     },
15695
15696     // private
15697     afterEdit : function(record){
15698         if(this.modified.indexOf(record) == -1){
15699             this.modified.push(record);
15700         }
15701         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15702     },
15703     
15704     // private
15705     afterReject : function(record){
15706         this.modified.remove(record);
15707         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15708     },
15709
15710     // private
15711     afterCommit : function(record){
15712         this.modified.remove(record);
15713         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15714     },
15715
15716     /**
15717      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15718      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15719      */
15720     commitChanges : function(){
15721         var m = this.modified.slice(0);
15722         this.modified = [];
15723         for(var i = 0, len = m.length; i < len; i++){
15724             m[i].commit();
15725         }
15726     },
15727
15728     /**
15729      * Cancel outstanding changes on all changed records.
15730      */
15731     rejectChanges : function(){
15732         var m = this.modified.slice(0);
15733         this.modified = [];
15734         for(var i = 0, len = m.length; i < len; i++){
15735             m[i].reject();
15736         }
15737     },
15738
15739     onMetaChange : function(meta, rtype, o){
15740         this.recordType = rtype;
15741         this.fields = rtype.prototype.fields;
15742         delete this.snapshot;
15743         this.sortInfo = meta.sortInfo || this.sortInfo;
15744         this.modified = [];
15745         this.fireEvent('metachange', this, this.reader.meta);
15746     },
15747     
15748     moveIndex : function(data, type)
15749     {
15750         var index = this.indexOf(data);
15751         
15752         var newIndex = index + type;
15753         
15754         this.remove(data);
15755         
15756         this.insert(newIndex, data);
15757         
15758     }
15759 });/*
15760  * Based on:
15761  * Ext JS Library 1.1.1
15762  * Copyright(c) 2006-2007, Ext JS, LLC.
15763  *
15764  * Originally Released Under LGPL - original licence link has changed is not relivant.
15765  *
15766  * Fork - LGPL
15767  * <script type="text/javascript">
15768  */
15769
15770 /**
15771  * @class Roo.data.SimpleStore
15772  * @extends Roo.data.Store
15773  * Small helper class to make creating Stores from Array data easier.
15774  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15775  * @cfg {Array} fields An array of field definition objects, or field name strings.
15776  * @cfg {Object} an existing reader (eg. copied from another store)
15777  * @cfg {Array} data The multi-dimensional array of data
15778  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15779  * @cfg {Roo.data.Reader} reader  [not-required] 
15780  * @constructor
15781  * @param {Object} config
15782  */
15783 Roo.data.SimpleStore = function(config)
15784 {
15785     Roo.data.SimpleStore.superclass.constructor.call(this, {
15786         isLocal : true,
15787         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15788                 id: config.id
15789             },
15790             Roo.data.Record.create(config.fields)
15791         ),
15792         proxy : new Roo.data.MemoryProxy(config.data)
15793     });
15794     this.load();
15795 };
15796 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15797  * Based on:
15798  * Ext JS Library 1.1.1
15799  * Copyright(c) 2006-2007, Ext JS, LLC.
15800  *
15801  * Originally Released Under LGPL - original licence link has changed is not relivant.
15802  *
15803  * Fork - LGPL
15804  * <script type="text/javascript">
15805  */
15806
15807 /**
15808 /**
15809  * @extends Roo.data.Store
15810  * @class Roo.data.JsonStore
15811  * Small helper class to make creating Stores for JSON data easier. <br/>
15812 <pre><code>
15813 var store = new Roo.data.JsonStore({
15814     url: 'get-images.php',
15815     root: 'images',
15816     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15817 });
15818 </code></pre>
15819  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15820  * JsonReader and HttpProxy (unless inline data is provided).</b>
15821  * @cfg {Array} fields An array of field definition objects, or field name strings.
15822  * @constructor
15823  * @param {Object} config
15824  */
15825 Roo.data.JsonStore = function(c){
15826     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15827         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15828         reader: new Roo.data.JsonReader(c, c.fields)
15829     }));
15830 };
15831 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842  
15843 Roo.data.Field = function(config){
15844     if(typeof config == "string"){
15845         config = {name: config};
15846     }
15847     Roo.apply(this, config);
15848     
15849     if(!this.type){
15850         this.type = "auto";
15851     }
15852     
15853     var st = Roo.data.SortTypes;
15854     // named sortTypes are supported, here we look them up
15855     if(typeof this.sortType == "string"){
15856         this.sortType = st[this.sortType];
15857     }
15858     
15859     // set default sortType for strings and dates
15860     if(!this.sortType){
15861         switch(this.type){
15862             case "string":
15863                 this.sortType = st.asUCString;
15864                 break;
15865             case "date":
15866                 this.sortType = st.asDate;
15867                 break;
15868             default:
15869                 this.sortType = st.none;
15870         }
15871     }
15872
15873     // define once
15874     var stripRe = /[\$,%]/g;
15875
15876     // prebuilt conversion function for this field, instead of
15877     // switching every time we're reading a value
15878     if(!this.convert){
15879         var cv, dateFormat = this.dateFormat;
15880         switch(this.type){
15881             case "":
15882             case "auto":
15883             case undefined:
15884                 cv = function(v){ return v; };
15885                 break;
15886             case "string":
15887                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15888                 break;
15889             case "int":
15890                 cv = function(v){
15891                     return v !== undefined && v !== null && v !== '' ?
15892                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15893                     };
15894                 break;
15895             case "float":
15896                 cv = function(v){
15897                     return v !== undefined && v !== null && v !== '' ?
15898                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15899                     };
15900                 break;
15901             case "bool":
15902             case "boolean":
15903                 cv = function(v){ return v === true || v === "true" || v == 1; };
15904                 break;
15905             case "date":
15906                 cv = function(v){
15907                     if(!v){
15908                         return '';
15909                     }
15910                     if(v instanceof Date){
15911                         return v;
15912                     }
15913                     if(dateFormat){
15914                         if(dateFormat == "timestamp"){
15915                             return new Date(v*1000);
15916                         }
15917                         return Date.parseDate(v, dateFormat);
15918                     }
15919                     var parsed = Date.parse(v);
15920                     return parsed ? new Date(parsed) : null;
15921                 };
15922              break;
15923             
15924         }
15925         this.convert = cv;
15926     }
15927 };
15928
15929 Roo.data.Field.prototype = {
15930     dateFormat: null,
15931     defaultValue: "",
15932     mapping: null,
15933     sortType : null,
15934     sortDir : "ASC"
15935 };/*
15936  * Based on:
15937  * Ext JS Library 1.1.1
15938  * Copyright(c) 2006-2007, Ext JS, LLC.
15939  *
15940  * Originally Released Under LGPL - original licence link has changed is not relivant.
15941  *
15942  * Fork - LGPL
15943  * <script type="text/javascript">
15944  */
15945  
15946 // Base class for reading structured data from a data source.  This class is intended to be
15947 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15948
15949 /**
15950  * @class Roo.data.DataReader
15951  * @abstract
15952  * Base class for reading structured data from a data source.  This class is intended to be
15953  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15954  */
15955
15956 Roo.data.DataReader = function(meta, recordType){
15957     
15958     this.meta = meta;
15959     
15960     this.recordType = recordType instanceof Array ? 
15961         Roo.data.Record.create(recordType) : recordType;
15962 };
15963
15964 Roo.data.DataReader.prototype = {
15965     
15966     
15967     readerType : 'Data',
15968      /**
15969      * Create an empty record
15970      * @param {Object} data (optional) - overlay some values
15971      * @return {Roo.data.Record} record created.
15972      */
15973     newRow :  function(d) {
15974         var da =  {};
15975         this.recordType.prototype.fields.each(function(c) {
15976             switch( c.type) {
15977                 case 'int' : da[c.name] = 0; break;
15978                 case 'date' : da[c.name] = new Date(); break;
15979                 case 'float' : da[c.name] = 0.0; break;
15980                 case 'boolean' : da[c.name] = false; break;
15981                 default : da[c.name] = ""; break;
15982             }
15983             
15984         });
15985         return new this.recordType(Roo.apply(da, d));
15986     }
15987     
15988     
15989 };/*
15990  * Based on:
15991  * Ext JS Library 1.1.1
15992  * Copyright(c) 2006-2007, Ext JS, LLC.
15993  *
15994  * Originally Released Under LGPL - original licence link has changed is not relivant.
15995  *
15996  * Fork - LGPL
15997  * <script type="text/javascript">
15998  */
15999
16000 /**
16001  * @class Roo.data.DataProxy
16002  * @extends Roo.util.Observable
16003  * @abstract
16004  * This class is an abstract base class for implementations which provide retrieval of
16005  * unformatted data objects.<br>
16006  * <p>
16007  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16008  * (of the appropriate type which knows how to parse the data object) to provide a block of
16009  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16010  * <p>
16011  * Custom implementations must implement the load method as described in
16012  * {@link Roo.data.HttpProxy#load}.
16013  */
16014 Roo.data.DataProxy = function(){
16015     this.addEvents({
16016         /**
16017          * @event beforeload
16018          * Fires before a network request is made to retrieve a data object.
16019          * @param {Object} This DataProxy object.
16020          * @param {Object} params The params parameter to the load function.
16021          */
16022         beforeload : true,
16023         /**
16024          * @event load
16025          * Fires before the load method's callback is called.
16026          * @param {Object} This DataProxy object.
16027          * @param {Object} o The data object.
16028          * @param {Object} arg The callback argument object passed to the load function.
16029          */
16030         load : true,
16031         /**
16032          * @event loadexception
16033          * Fires if an Exception occurs during data retrieval.
16034          * @param {Object} This DataProxy object.
16035          * @param {Object} o The data object.
16036          * @param {Object} arg The callback argument object passed to the load function.
16037          * @param {Object} e The Exception.
16038          */
16039         loadexception : true
16040     });
16041     Roo.data.DataProxy.superclass.constructor.call(this);
16042 };
16043
16044 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16045
16046     /**
16047      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16048      */
16049 /*
16050  * Based on:
16051  * Ext JS Library 1.1.1
16052  * Copyright(c) 2006-2007, Ext JS, LLC.
16053  *
16054  * Originally Released Under LGPL - original licence link has changed is not relivant.
16055  *
16056  * Fork - LGPL
16057  * <script type="text/javascript">
16058  */
16059 /**
16060  * @class Roo.data.MemoryProxy
16061  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16062  * to the Reader when its load method is called.
16063  * @constructor
16064  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16065  */
16066 Roo.data.MemoryProxy = function(data){
16067     if (data.data) {
16068         data = data.data;
16069     }
16070     Roo.data.MemoryProxy.superclass.constructor.call(this);
16071     this.data = data;
16072 };
16073
16074 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16075     
16076     /**
16077      * Load data from the requested source (in this case an in-memory
16078      * data object passed to the constructor), read the data object into
16079      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16080      * process that block using the passed callback.
16081      * @param {Object} params This parameter is not used by the MemoryProxy class.
16082      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16083      * object into a block of Roo.data.Records.
16084      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16085      * The function must be passed <ul>
16086      * <li>The Record block object</li>
16087      * <li>The "arg" argument from the load function</li>
16088      * <li>A boolean success indicator</li>
16089      * </ul>
16090      * @param {Object} scope The scope in which to call the callback
16091      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16092      */
16093     load : function(params, reader, callback, scope, arg){
16094         params = params || {};
16095         var result;
16096         try {
16097             result = reader.readRecords(params.data ? params.data :this.data);
16098         }catch(e){
16099             this.fireEvent("loadexception", this, arg, null, e);
16100             callback.call(scope, null, arg, false);
16101             return;
16102         }
16103         callback.call(scope, result, arg, true);
16104     },
16105     
16106     // private
16107     update : function(params, records){
16108         
16109     }
16110 });/*
16111  * Based on:
16112  * Ext JS Library 1.1.1
16113  * Copyright(c) 2006-2007, Ext JS, LLC.
16114  *
16115  * Originally Released Under LGPL - original licence link has changed is not relivant.
16116  *
16117  * Fork - LGPL
16118  * <script type="text/javascript">
16119  */
16120 /**
16121  * @class Roo.data.HttpProxy
16122  * @extends Roo.data.DataProxy
16123  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16124  * configured to reference a certain URL.<br><br>
16125  * <p>
16126  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16127  * from which the running page was served.<br><br>
16128  * <p>
16129  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16130  * <p>
16131  * Be aware that to enable the browser to parse an XML document, the server must set
16132  * the Content-Type header in the HTTP response to "text/xml".
16133  * @constructor
16134  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16135  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16136  * will be used to make the request.
16137  */
16138 Roo.data.HttpProxy = function(conn){
16139     Roo.data.HttpProxy.superclass.constructor.call(this);
16140     // is conn a conn config or a real conn?
16141     this.conn = conn;
16142     this.useAjax = !conn || !conn.events;
16143   
16144 };
16145
16146 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16147     // thse are take from connection...
16148     
16149     /**
16150      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16151      */
16152     /**
16153      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16154      * extra parameters to each request made by this object. (defaults to undefined)
16155      */
16156     /**
16157      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16158      *  to each request made by this object. (defaults to undefined)
16159      */
16160     /**
16161      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16162      */
16163     /**
16164      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16165      */
16166      /**
16167      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16168      * @type Boolean
16169      */
16170   
16171
16172     /**
16173      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16174      * @type Boolean
16175      */
16176     /**
16177      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16178      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16179      * a finer-grained basis than the DataProxy events.
16180      */
16181     getConnection : function(){
16182         return this.useAjax ? Roo.Ajax : this.conn;
16183     },
16184
16185     /**
16186      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16187      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16188      * process that block using the passed callback.
16189      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16190      * for the request to the remote server.
16191      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16192      * object into a block of Roo.data.Records.
16193      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16194      * The function must be passed <ul>
16195      * <li>The Record block object</li>
16196      * <li>The "arg" argument from the load function</li>
16197      * <li>A boolean success indicator</li>
16198      * </ul>
16199      * @param {Object} scope The scope in which to call the callback
16200      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16201      */
16202     load : function(params, reader, callback, scope, arg){
16203         if(this.fireEvent("beforeload", this, params) !== false){
16204             var  o = {
16205                 params : params || {},
16206                 request: {
16207                     callback : callback,
16208                     scope : scope,
16209                     arg : arg
16210                 },
16211                 reader: reader,
16212                 callback : this.loadResponse,
16213                 scope: this
16214             };
16215             if(this.useAjax){
16216                 Roo.applyIf(o, this.conn);
16217                 if(this.activeRequest){
16218                     Roo.Ajax.abort(this.activeRequest);
16219                 }
16220                 this.activeRequest = Roo.Ajax.request(o);
16221             }else{
16222                 this.conn.request(o);
16223             }
16224         }else{
16225             callback.call(scope||this, null, arg, false);
16226         }
16227     },
16228
16229     // private
16230     loadResponse : function(o, success, response){
16231         delete this.activeRequest;
16232         if(!success){
16233             this.fireEvent("loadexception", this, o, response);
16234             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16235             return;
16236         }
16237         var result;
16238         try {
16239             result = o.reader.read(response);
16240         }catch(e){
16241             this.fireEvent("loadexception", this, o, response, e);
16242             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16243             return;
16244         }
16245         
16246         this.fireEvent("load", this, o, o.request.arg);
16247         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16248     },
16249
16250     // private
16251     update : function(dataSet){
16252
16253     },
16254
16255     // private
16256     updateResponse : function(dataSet){
16257
16258     }
16259 });/*
16260  * Based on:
16261  * Ext JS Library 1.1.1
16262  * Copyright(c) 2006-2007, Ext JS, LLC.
16263  *
16264  * Originally Released Under LGPL - original licence link has changed is not relivant.
16265  *
16266  * Fork - LGPL
16267  * <script type="text/javascript">
16268  */
16269
16270 /**
16271  * @class Roo.data.ScriptTagProxy
16272  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16273  * other than the originating domain of the running page.<br><br>
16274  * <p>
16275  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
16276  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16277  * <p>
16278  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16279  * source code that is used as the source inside a &lt;script> tag.<br><br>
16280  * <p>
16281  * In order for the browser to process the returned data, the server must wrap the data object
16282  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16283  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16284  * depending on whether the callback name was passed:
16285  * <p>
16286  * <pre><code>
16287 boolean scriptTag = false;
16288 String cb = request.getParameter("callback");
16289 if (cb != null) {
16290     scriptTag = true;
16291     response.setContentType("text/javascript");
16292 } else {
16293     response.setContentType("application/x-json");
16294 }
16295 Writer out = response.getWriter();
16296 if (scriptTag) {
16297     out.write(cb + "(");
16298 }
16299 out.print(dataBlock.toJsonString());
16300 if (scriptTag) {
16301     out.write(");");
16302 }
16303 </pre></code>
16304  *
16305  * @constructor
16306  * @param {Object} config A configuration object.
16307  */
16308 Roo.data.ScriptTagProxy = function(config){
16309     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16310     Roo.apply(this, config);
16311     this.head = document.getElementsByTagName("head")[0];
16312 };
16313
16314 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16315
16316 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16317     /**
16318      * @cfg {String} url The URL from which to request the data object.
16319      */
16320     /**
16321      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16322      */
16323     timeout : 30000,
16324     /**
16325      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16326      * the server the name of the callback function set up by the load call to process the returned data object.
16327      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16328      * javascript output which calls this named function passing the data object as its only parameter.
16329      */
16330     callbackParam : "callback",
16331     /**
16332      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16333      * name to the request.
16334      */
16335     nocache : true,
16336
16337     /**
16338      * Load data from the configured URL, read the data object into
16339      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16340      * process that block using the passed callback.
16341      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16342      * for the request to the remote server.
16343      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16344      * object into a block of Roo.data.Records.
16345      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16346      * The function must be passed <ul>
16347      * <li>The Record block object</li>
16348      * <li>The "arg" argument from the load function</li>
16349      * <li>A boolean success indicator</li>
16350      * </ul>
16351      * @param {Object} scope The scope in which to call the callback
16352      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16353      */
16354     load : function(params, reader, callback, scope, arg){
16355         if(this.fireEvent("beforeload", this, params) !== false){
16356
16357             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16358
16359             var url = this.url;
16360             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16361             if(this.nocache){
16362                 url += "&_dc=" + (new Date().getTime());
16363             }
16364             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16365             var trans = {
16366                 id : transId,
16367                 cb : "stcCallback"+transId,
16368                 scriptId : "stcScript"+transId,
16369                 params : params,
16370                 arg : arg,
16371                 url : url,
16372                 callback : callback,
16373                 scope : scope,
16374                 reader : reader
16375             };
16376             var conn = this;
16377
16378             window[trans.cb] = function(o){
16379                 conn.handleResponse(o, trans);
16380             };
16381
16382             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16383
16384             if(this.autoAbort !== false){
16385                 this.abort();
16386             }
16387
16388             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16389
16390             var script = document.createElement("script");
16391             script.setAttribute("src", url);
16392             script.setAttribute("type", "text/javascript");
16393             script.setAttribute("id", trans.scriptId);
16394             this.head.appendChild(script);
16395
16396             this.trans = trans;
16397         }else{
16398             callback.call(scope||this, null, arg, false);
16399         }
16400     },
16401
16402     // private
16403     isLoading : function(){
16404         return this.trans ? true : false;
16405     },
16406
16407     /**
16408      * Abort the current server request.
16409      */
16410     abort : function(){
16411         if(this.isLoading()){
16412             this.destroyTrans(this.trans);
16413         }
16414     },
16415
16416     // private
16417     destroyTrans : function(trans, isLoaded){
16418         this.head.removeChild(document.getElementById(trans.scriptId));
16419         clearTimeout(trans.timeoutId);
16420         if(isLoaded){
16421             window[trans.cb] = undefined;
16422             try{
16423                 delete window[trans.cb];
16424             }catch(e){}
16425         }else{
16426             // if hasn't been loaded, wait for load to remove it to prevent script error
16427             window[trans.cb] = function(){
16428                 window[trans.cb] = undefined;
16429                 try{
16430                     delete window[trans.cb];
16431                 }catch(e){}
16432             };
16433         }
16434     },
16435
16436     // private
16437     handleResponse : function(o, trans){
16438         this.trans = false;
16439         this.destroyTrans(trans, true);
16440         var result;
16441         try {
16442             result = trans.reader.readRecords(o);
16443         }catch(e){
16444             this.fireEvent("loadexception", this, o, trans.arg, e);
16445             trans.callback.call(trans.scope||window, null, trans.arg, false);
16446             return;
16447         }
16448         this.fireEvent("load", this, o, trans.arg);
16449         trans.callback.call(trans.scope||window, result, trans.arg, true);
16450     },
16451
16452     // private
16453     handleFailure : function(trans){
16454         this.trans = false;
16455         this.destroyTrans(trans, false);
16456         this.fireEvent("loadexception", this, null, trans.arg);
16457         trans.callback.call(trans.scope||window, null, trans.arg, false);
16458     }
16459 });/*
16460  * Based on:
16461  * Ext JS Library 1.1.1
16462  * Copyright(c) 2006-2007, Ext JS, LLC.
16463  *
16464  * Originally Released Under LGPL - original licence link has changed is not relivant.
16465  *
16466  * Fork - LGPL
16467  * <script type="text/javascript">
16468  */
16469
16470 /**
16471  * @class Roo.data.JsonReader
16472  * @extends Roo.data.DataReader
16473  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16474  * based on mappings in a provided Roo.data.Record constructor.
16475  * 
16476  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16477  * in the reply previously. 
16478  * 
16479  * <p>
16480  * Example code:
16481  * <pre><code>
16482 var RecordDef = Roo.data.Record.create([
16483     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16484     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16485 ]);
16486 var myReader = new Roo.data.JsonReader({
16487     totalProperty: "results",    // The property which contains the total dataset size (optional)
16488     root: "rows",                // The property which contains an Array of row objects
16489     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16490 }, RecordDef);
16491 </code></pre>
16492  * <p>
16493  * This would consume a JSON file like this:
16494  * <pre><code>
16495 { 'results': 2, 'rows': [
16496     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16497     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16498 }
16499 </code></pre>
16500  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16501  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16502  * paged from the remote server.
16503  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16504  * @cfg {String} root name of the property which contains the Array of row objects.
16505  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16506  * @cfg {Array} fields Array of field definition objects
16507  * @constructor
16508  * Create a new JsonReader
16509  * @param {Object} meta Metadata configuration options
16510  * @param {Object} recordType Either an Array of field definition objects,
16511  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16512  */
16513 Roo.data.JsonReader = function(meta, recordType){
16514     
16515     meta = meta || {};
16516     // set some defaults:
16517     Roo.applyIf(meta, {
16518         totalProperty: 'total',
16519         successProperty : 'success',
16520         root : 'data',
16521         id : 'id'
16522     });
16523     
16524     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16525 };
16526 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16527     
16528     readerType : 'Json',
16529     
16530     /**
16531      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16532      * Used by Store query builder to append _requestMeta to params.
16533      * 
16534      */
16535     metaFromRemote : false,
16536     /**
16537      * This method is only used by a DataProxy which has retrieved data from a remote server.
16538      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16539      * @return {Object} data A data block which is used by an Roo.data.Store object as
16540      * a cache of Roo.data.Records.
16541      */
16542     read : function(response){
16543         var json = response.responseText;
16544        
16545         var o = /* eval:var:o */ eval("("+json+")");
16546         if(!o) {
16547             throw {message: "JsonReader.read: Json object not found"};
16548         }
16549         
16550         if(o.metaData){
16551             
16552             delete this.ef;
16553             this.metaFromRemote = true;
16554             this.meta = o.metaData;
16555             this.recordType = Roo.data.Record.create(o.metaData.fields);
16556             this.onMetaChange(this.meta, this.recordType, o);
16557         }
16558         return this.readRecords(o);
16559     },
16560
16561     // private function a store will implement
16562     onMetaChange : function(meta, recordType, o){
16563
16564     },
16565
16566     /**
16567          * @ignore
16568          */
16569     simpleAccess: function(obj, subsc) {
16570         return obj[subsc];
16571     },
16572
16573         /**
16574          * @ignore
16575          */
16576     getJsonAccessor: function(){
16577         var re = /[\[\.]/;
16578         return function(expr) {
16579             try {
16580                 return(re.test(expr))
16581                     ? new Function("obj", "return obj." + expr)
16582                     : function(obj){
16583                         return obj[expr];
16584                     };
16585             } catch(e){}
16586             return Roo.emptyFn;
16587         };
16588     }(),
16589
16590     /**
16591      * Create a data block containing Roo.data.Records from an XML document.
16592      * @param {Object} o An object which contains an Array of row objects in the property specified
16593      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16594      * which contains the total size of the dataset.
16595      * @return {Object} data A data block which is used by an Roo.data.Store object as
16596      * a cache of Roo.data.Records.
16597      */
16598     readRecords : function(o){
16599         /**
16600          * After any data loads, the raw JSON data is available for further custom processing.
16601          * @type Object
16602          */
16603         this.o = o;
16604         var s = this.meta, Record = this.recordType,
16605             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16606
16607 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16608         if (!this.ef) {
16609             if(s.totalProperty) {
16610                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16611                 }
16612                 if(s.successProperty) {
16613                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16614                 }
16615                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16616                 if (s.id) {
16617                         var g = this.getJsonAccessor(s.id);
16618                         this.getId = function(rec) {
16619                                 var r = g(rec);  
16620                                 return (r === undefined || r === "") ? null : r;
16621                         };
16622                 } else {
16623                         this.getId = function(){return null;};
16624                 }
16625             this.ef = [];
16626             for(var jj = 0; jj < fl; jj++){
16627                 f = fi[jj];
16628                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16629                 this.ef[jj] = this.getJsonAccessor(map);
16630             }
16631         }
16632
16633         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16634         if(s.totalProperty){
16635             var vt = parseInt(this.getTotal(o), 10);
16636             if(!isNaN(vt)){
16637                 totalRecords = vt;
16638             }
16639         }
16640         if(s.successProperty){
16641             var vs = this.getSuccess(o);
16642             if(vs === false || vs === 'false'){
16643                 success = false;
16644             }
16645         }
16646         var records = [];
16647         for(var i = 0; i < c; i++){
16648                 var n = root[i];
16649             var values = {};
16650             var id = this.getId(n);
16651             for(var j = 0; j < fl; j++){
16652                 f = fi[j];
16653             var v = this.ef[j](n);
16654             if (!f.convert) {
16655                 Roo.log('missing convert for ' + f.name);
16656                 Roo.log(f);
16657                 continue;
16658             }
16659             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16660             }
16661             var record = new Record(values, id);
16662             record.json = n;
16663             records[i] = record;
16664         }
16665         return {
16666             raw : o,
16667             success : success,
16668             records : records,
16669             totalRecords : totalRecords
16670         };
16671     },
16672     // used when loading children.. @see loadDataFromChildren
16673     toLoadData: function(rec)
16674     {
16675         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16676         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16677         return { data : data, total : data.length };
16678         
16679     }
16680 });/*
16681  * Based on:
16682  * Ext JS Library 1.1.1
16683  * Copyright(c) 2006-2007, Ext JS, LLC.
16684  *
16685  * Originally Released Under LGPL - original licence link has changed is not relivant.
16686  *
16687  * Fork - LGPL
16688  * <script type="text/javascript">
16689  */
16690
16691 /**
16692  * @class Roo.data.ArrayReader
16693  * @extends Roo.data.DataReader
16694  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16695  * Each element of that Array represents a row of data fields. The
16696  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16697  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16698  * <p>
16699  * Example code:.
16700  * <pre><code>
16701 var RecordDef = Roo.data.Record.create([
16702     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16703     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16704 ]);
16705 var myReader = new Roo.data.ArrayReader({
16706     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16707 }, RecordDef);
16708 </code></pre>
16709  * <p>
16710  * This would consume an Array like this:
16711  * <pre><code>
16712 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16713   </code></pre>
16714  
16715  * @constructor
16716  * Create a new JsonReader
16717  * @param {Object} meta Metadata configuration options.
16718  * @param {Object|Array} recordType Either an Array of field definition objects
16719  * 
16720  * @cfg {Array} fields Array of field definition objects
16721  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16722  * as specified to {@link Roo.data.Record#create},
16723  * or an {@link Roo.data.Record} object
16724  *
16725  * 
16726  * created using {@link Roo.data.Record#create}.
16727  */
16728 Roo.data.ArrayReader = function(meta, recordType)
16729 {    
16730     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16731 };
16732
16733 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16734     
16735       /**
16736      * Create a data block containing Roo.data.Records from an XML document.
16737      * @param {Object} o An Array of row objects which represents the dataset.
16738      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16739      * a cache of Roo.data.Records.
16740      */
16741     readRecords : function(o)
16742     {
16743         var sid = this.meta ? this.meta.id : null;
16744         var recordType = this.recordType, fields = recordType.prototype.fields;
16745         var records = [];
16746         var root = o;
16747         for(var i = 0; i < root.length; i++){
16748             var n = root[i];
16749             var values = {};
16750             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16751             for(var j = 0, jlen = fields.length; j < jlen; j++){
16752                 var f = fields.items[j];
16753                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16754                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16755                 v = f.convert(v);
16756                 values[f.name] = v;
16757             }
16758             var record = new recordType(values, id);
16759             record.json = n;
16760             records[records.length] = record;
16761         }
16762         return {
16763             records : records,
16764             totalRecords : records.length
16765         };
16766     },
16767     // used when loading children.. @see loadDataFromChildren
16768     toLoadData: function(rec)
16769     {
16770         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16771         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16772         
16773     }
16774     
16775     
16776 });/*
16777  * - LGPL
16778  * * 
16779  */
16780
16781 /**
16782  * @class Roo.bootstrap.form.ComboBox
16783  * @extends Roo.bootstrap.form.TriggerField
16784  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16785  * @cfg {Boolean} append (true|false) default false
16786  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16787  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16788  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16789  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16790  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16791  * @cfg {Boolean} animate default true
16792  * @cfg {Boolean} emptyResultText only for touch device
16793  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16794  * @cfg {String} emptyTitle default ''
16795  * @cfg {Number} width fixed with? experimental
16796  * @constructor
16797  * Create a new ComboBox.
16798  * @param {Object} config Configuration options
16799  */
16800 Roo.bootstrap.form.ComboBox = function(config){
16801     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16802     this.addEvents({
16803         /**
16804          * @event expand
16805          * Fires when the dropdown list is expanded
16806         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16807         */
16808         'expand' : true,
16809         /**
16810          * @event collapse
16811          * Fires when the dropdown list is collapsed
16812         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16813         */
16814         'collapse' : true,
16815         /**
16816          * @event beforeselect
16817          * Fires before a list item is selected. Return false to cancel the selection.
16818         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16819         * @param {Roo.data.Record} record The data record returned from the underlying store
16820         * @param {Number} index The index of the selected item in the dropdown list
16821         */
16822         'beforeselect' : true,
16823         /**
16824          * @event select
16825          * Fires when a list item is selected
16826         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16827         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16828         * @param {Number} index The index of the selected item in the dropdown list
16829         */
16830         'select' : true,
16831         /**
16832          * @event beforequery
16833          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16834          * The event object passed has these properties:
16835         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16836         * @param {String} query The query
16837         * @param {Boolean} forceAll true to force "all" query
16838         * @param {Boolean} cancel true to cancel the query
16839         * @param {Object} e The query event object
16840         */
16841         'beforequery': true,
16842          /**
16843          * @event add
16844          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16845         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16846         */
16847         'add' : true,
16848         /**
16849          * @event edit
16850          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16851         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16852         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16853         */
16854         'edit' : true,
16855         /**
16856          * @event remove
16857          * Fires when the remove value from the combobox array
16858         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859         */
16860         'remove' : true,
16861         /**
16862          * @event afterremove
16863          * Fires when the remove value from the combobox array
16864         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16865         */
16866         'afterremove' : true,
16867         /**
16868          * @event specialfilter
16869          * Fires when specialfilter
16870             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871             */
16872         'specialfilter' : true,
16873         /**
16874          * @event tick
16875          * Fires when tick the element
16876             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877             */
16878         'tick' : true,
16879         /**
16880          * @event touchviewdisplay
16881          * Fires when touch view require special display (default is using displayField)
16882             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16883             * @param {Object} cfg set html .
16884             */
16885         'touchviewdisplay' : true
16886         
16887     });
16888     
16889     this.item = [];
16890     this.tickItems = [];
16891     
16892     this.selectedIndex = -1;
16893     if(this.mode == 'local'){
16894         if(config.queryDelay === undefined){
16895             this.queryDelay = 10;
16896         }
16897         if(config.minChars === undefined){
16898             this.minChars = 0;
16899         }
16900     }
16901 };
16902
16903 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16904      
16905     /**
16906      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16907      * rendering into an Roo.Editor, defaults to false)
16908      */
16909     /**
16910      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16911      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16912      */
16913     /**
16914      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16915      */
16916     /**
16917      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16918      * the dropdown list (defaults to undefined, with no header element)
16919      */
16920
16921      /**
16922      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16923      */
16924      
16925      /**
16926      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16927      */
16928     listWidth: undefined,
16929     /**
16930      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16931      * mode = 'remote' or 'text' if mode = 'local')
16932      */
16933     displayField: undefined,
16934     
16935     /**
16936      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16937      * mode = 'remote' or 'value' if mode = 'local'). 
16938      * Note: use of a valueField requires the user make a selection
16939      * in order for a value to be mapped.
16940      */
16941     valueField: undefined,
16942     /**
16943      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16944      */
16945     modalTitle : '',
16946     
16947     /**
16948      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16949      * field's data value (defaults to the underlying DOM element's name)
16950      */
16951     hiddenName: undefined,
16952     /**
16953      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16954      */
16955     listClass: '',
16956     /**
16957      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16958      */
16959     selectedClass: 'active',
16960     
16961     /**
16962      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16963      */
16964     shadow:'sides',
16965     /**
16966      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16967      * anchor positions (defaults to 'tl-bl')
16968      */
16969     listAlign: 'tl-bl?',
16970     /**
16971      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16972      */
16973     maxHeight: 300,
16974     /**
16975      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16976      * query specified by the allQuery config option (defaults to 'query')
16977      */
16978     triggerAction: 'query',
16979     /**
16980      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16981      * (defaults to 4, does not apply if editable = false)
16982      */
16983     minChars : 4,
16984     /**
16985      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16986      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16987      */
16988     typeAhead: false,
16989     /**
16990      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16991      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16992      */
16993     queryDelay: 500,
16994     /**
16995      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16996      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16997      */
16998     pageSize: 0,
16999     /**
17000      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17001      * when editable = true (defaults to false)
17002      */
17003     selectOnFocus:false,
17004     /**
17005      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17006      */
17007     queryParam: 'query',
17008     /**
17009      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17010      * when mode = 'remote' (defaults to 'Loading...')
17011      */
17012     loadingText: 'Loading...',
17013     /**
17014      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17015      */
17016     resizable: false,
17017     /**
17018      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17019      */
17020     handleHeight : 8,
17021     /**
17022      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17023      * traditional select (defaults to true)
17024      */
17025     editable: true,
17026     /**
17027      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17028      */
17029     allQuery: '',
17030     /**
17031      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17032      */
17033     mode: 'remote',
17034     /**
17035      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17036      * listWidth has a higher value)
17037      */
17038     minListWidth : 70,
17039     /**
17040      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17041      * allow the user to set arbitrary text into the field (defaults to false)
17042      */
17043     forceSelection:false,
17044     /**
17045      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17046      * if typeAhead = true (defaults to 250)
17047      */
17048     typeAheadDelay : 250,
17049     /**
17050      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17051      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17052      */
17053     valueNotFoundText : undefined,
17054     /**
17055      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17056      */
17057     blockFocus : false,
17058     
17059     /**
17060      * @cfg {Boolean} disableClear Disable showing of clear button.
17061      */
17062     disableClear : false,
17063     /**
17064      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17065      */
17066     alwaysQuery : false,
17067     
17068     /**
17069      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17070      */
17071     multiple : false,
17072     
17073     /**
17074      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17075      */
17076     invalidClass : "has-warning",
17077     
17078     /**
17079      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17080      */
17081     validClass : "has-success",
17082     
17083     /**
17084      * @cfg {Boolean} specialFilter (true|false) special filter default false
17085      */
17086     specialFilter : false,
17087     
17088     /**
17089      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17090      */
17091     mobileTouchView : true,
17092     
17093     /**
17094      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17095      */
17096     useNativeIOS : false,
17097     
17098     /**
17099      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17100      */
17101     mobile_restrict_height : false,
17102     
17103     ios_options : false,
17104     
17105     //private
17106     addicon : false,
17107     editicon: false,
17108     
17109     page: 0,
17110     hasQuery: false,
17111     append: false,
17112     loadNext: false,
17113     autoFocus : true,
17114     tickable : false,
17115     btnPosition : 'right',
17116     triggerList : true,
17117     showToggleBtn : true,
17118     animate : true,
17119     emptyResultText: 'Empty',
17120     triggerText : 'Select',
17121     emptyTitle : '',
17122     width : false,
17123     
17124     // element that contains real text value.. (when hidden is used..)
17125     
17126     getAutoCreate : function()
17127     {   
17128         var cfg = false;
17129         //render
17130         /*
17131          * Render classic select for iso
17132          */
17133         
17134         if(Roo.isIOS && this.useNativeIOS){
17135             cfg = this.getAutoCreateNativeIOS();
17136             return cfg;
17137         }
17138         
17139         /*
17140          * Touch Devices
17141          */
17142         
17143         if(Roo.isTouch && this.mobileTouchView){
17144             cfg = this.getAutoCreateTouchView();
17145             return cfg;;
17146         }
17147         
17148         /*
17149          *  Normal ComboBox
17150          */
17151         if(!this.tickable){
17152             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17153             return cfg;
17154         }
17155         
17156         /*
17157          *  ComboBox with tickable selections
17158          */
17159              
17160         var align = this.labelAlign || this.parentLabelAlign();
17161         
17162         cfg = {
17163             cls : 'form-group roo-combobox-tickable' //input-group
17164         };
17165         
17166         var btn_text_select = '';
17167         var btn_text_done = '';
17168         var btn_text_cancel = '';
17169         
17170         if (this.btn_text_show) {
17171             btn_text_select = 'Select';
17172             btn_text_done = 'Done';
17173             btn_text_cancel = 'Cancel'; 
17174         }
17175         
17176         var buttons = {
17177             tag : 'div',
17178             cls : 'tickable-buttons',
17179             cn : [
17180                 {
17181                     tag : 'button',
17182                     type : 'button',
17183                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17184                     //html : this.triggerText
17185                     html: btn_text_select
17186                 },
17187                 {
17188                     tag : 'button',
17189                     type : 'button',
17190                     name : 'ok',
17191                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17192                     //html : 'Done'
17193                     html: btn_text_done
17194                 },
17195                 {
17196                     tag : 'button',
17197                     type : 'button',
17198                     name : 'cancel',
17199                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17200                     //html : 'Cancel'
17201                     html: btn_text_cancel
17202                 }
17203             ]
17204         };
17205         
17206         if(this.editable){
17207             buttons.cn.unshift({
17208                 tag: 'input',
17209                 cls: 'roo-select2-search-field-input'
17210             });
17211         }
17212         
17213         var _this = this;
17214         
17215         Roo.each(buttons.cn, function(c){
17216             if (_this.size) {
17217                 c.cls += ' btn-' + _this.size;
17218             }
17219
17220             if (_this.disabled) {
17221                 c.disabled = true;
17222             }
17223         });
17224         
17225         var box = {
17226             tag: 'div',
17227             style : 'display: contents',
17228             cn: [
17229                 {
17230                     tag: 'input',
17231                     type : 'hidden',
17232                     cls: 'form-hidden-field'
17233                 },
17234                 {
17235                     tag: 'ul',
17236                     cls: 'roo-select2-choices',
17237                     cn:[
17238                         {
17239                             tag: 'li',
17240                             cls: 'roo-select2-search-field',
17241                             cn: [
17242                                 buttons
17243                             ]
17244                         }
17245                     ]
17246                 }
17247             ]
17248         };
17249         
17250         var combobox = {
17251             cls: 'roo-select2-container input-group roo-select2-container-multi',
17252             cn: [
17253                 
17254                 box
17255 //                {
17256 //                    tag: 'ul',
17257 //                    cls: 'typeahead typeahead-long dropdown-menu',
17258 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17259 //                }
17260             ]
17261         };
17262         
17263         if(this.hasFeedback && !this.allowBlank){
17264             
17265             var feedback = {
17266                 tag: 'span',
17267                 cls: 'glyphicon form-control-feedback'
17268             };
17269
17270             combobox.cn.push(feedback);
17271         }
17272         
17273         
17274         
17275         var indicator = {
17276             tag : 'i',
17277             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17278             tooltip : 'This field is required'
17279         };
17280         if (Roo.bootstrap.version == 4) {
17281             indicator = {
17282                 tag : 'i',
17283                 style : 'display:none'
17284             };
17285         }
17286         if (align ==='left' && this.fieldLabel.length) {
17287             
17288             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17289             
17290             cfg.cn = [
17291                 indicator,
17292                 {
17293                     tag: 'label',
17294                     'for' :  id,
17295                     cls : 'control-label col-form-label',
17296                     html : this.fieldLabel
17297
17298                 },
17299                 {
17300                     cls : "", 
17301                     cn: [
17302                         combobox
17303                     ]
17304                 }
17305
17306             ];
17307             
17308             var labelCfg = cfg.cn[1];
17309             var contentCfg = cfg.cn[2];
17310             
17311
17312             if(this.indicatorpos == 'right'){
17313                 
17314                 cfg.cn = [
17315                     {
17316                         tag: 'label',
17317                         'for' :  id,
17318                         cls : 'control-label col-form-label',
17319                         cn : [
17320                             {
17321                                 tag : 'span',
17322                                 html : this.fieldLabel
17323                             },
17324                             indicator
17325                         ]
17326                     },
17327                     {
17328                         cls : "",
17329                         cn: [
17330                             combobox
17331                         ]
17332                     }
17333
17334                 ];
17335                 
17336                 
17337                 
17338                 labelCfg = cfg.cn[0];
17339                 contentCfg = cfg.cn[1];
17340             
17341             }
17342             
17343             if(this.labelWidth > 12){
17344                 labelCfg.style = "width: " + this.labelWidth + 'px';
17345             }
17346             if(this.width * 1 > 0){
17347                 contentCfg.style = "width: " + this.width + 'px';
17348             }
17349             if(this.labelWidth < 13 && this.labelmd == 0){
17350                 this.labelmd = this.labelWidth;
17351             }
17352             
17353             if(this.labellg > 0){
17354                 labelCfg.cls += ' col-lg-' + this.labellg;
17355                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17356             }
17357             
17358             if(this.labelmd > 0){
17359                 labelCfg.cls += ' col-md-' + this.labelmd;
17360                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17361             }
17362             
17363             if(this.labelsm > 0){
17364                 labelCfg.cls += ' col-sm-' + this.labelsm;
17365                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17366             }
17367             
17368             if(this.labelxs > 0){
17369                 labelCfg.cls += ' col-xs-' + this.labelxs;
17370                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17371             }
17372                 
17373                 
17374         } else if ( this.fieldLabel.length) {
17375 //                Roo.log(" label");
17376                  cfg.cn = [
17377                    indicator,
17378                     {
17379                         tag: 'label',
17380                         //cls : 'input-group-addon',
17381                         html : this.fieldLabel
17382                     },
17383                     combobox
17384                 ];
17385                 
17386                 if(this.indicatorpos == 'right'){
17387                     cfg.cn = [
17388                         {
17389                             tag: 'label',
17390                             //cls : 'input-group-addon',
17391                             html : this.fieldLabel
17392                         },
17393                         indicator,
17394                         combobox
17395                     ];
17396                     
17397                 }
17398
17399         } else {
17400             
17401 //                Roo.log(" no label && no align");
17402                 cfg = combobox
17403                      
17404                 
17405         }
17406          
17407         var settings=this;
17408         ['xs','sm','md','lg'].map(function(size){
17409             if (settings[size]) {
17410                 cfg.cls += ' col-' + size + '-' + settings[size];
17411             }
17412         });
17413         
17414         return cfg;
17415         
17416     },
17417     
17418     _initEventsCalled : false,
17419     
17420     // private
17421     initEvents: function()
17422     {   
17423         if (this._initEventsCalled) { // as we call render... prevent looping...
17424             return;
17425         }
17426         this._initEventsCalled = true;
17427         
17428         if (!this.store) {
17429             throw "can not find store for combo";
17430         }
17431         
17432         this.indicator = this.indicatorEl();
17433         
17434         this.store = Roo.factory(this.store, Roo.data);
17435         this.store.parent = this;
17436         
17437         // if we are building from html. then this element is so complex, that we can not really
17438         // use the rendered HTML.
17439         // so we have to trash and replace the previous code.
17440         if (Roo.XComponent.build_from_html) {
17441             // remove this element....
17442             var e = this.el.dom, k=0;
17443             while (e ) { e = e.previousSibling;  ++k;}
17444
17445             this.el.remove();
17446             
17447             this.el=false;
17448             this.rendered = false;
17449             
17450             this.render(this.parent().getChildContainer(true), k);
17451         }
17452         
17453         if(Roo.isIOS && this.useNativeIOS){
17454             this.initIOSView();
17455             return;
17456         }
17457         
17458         /*
17459          * Touch Devices
17460          */
17461         
17462         if(Roo.isTouch && this.mobileTouchView){
17463             this.initTouchView();
17464             return;
17465         }
17466         
17467         if(this.tickable){
17468             this.initTickableEvents();
17469             return;
17470         }
17471         
17472         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17473         
17474         if(this.hiddenName){
17475             
17476             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17477             
17478             this.hiddenField.dom.value =
17479                 this.hiddenValue !== undefined ? this.hiddenValue :
17480                 this.value !== undefined ? this.value : '';
17481
17482             // prevent input submission
17483             this.el.dom.removeAttribute('name');
17484             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17485              
17486              
17487         }
17488         //if(Roo.isGecko){
17489         //    this.el.dom.setAttribute('autocomplete', 'off');
17490         //}
17491         
17492         var cls = 'x-combo-list';
17493         
17494         //this.list = new Roo.Layer({
17495         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17496         //});
17497         
17498         var _this = this;
17499         
17500         (function(){
17501             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17502             _this.list.setWidth(lw);
17503         }).defer(100);
17504         
17505         this.list.on('mouseover', this.onViewOver, this);
17506         this.list.on('mousemove', this.onViewMove, this);
17507         this.list.on('scroll', this.onViewScroll, this);
17508         
17509         /*
17510         this.list.swallowEvent('mousewheel');
17511         this.assetHeight = 0;
17512
17513         if(this.title){
17514             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17515             this.assetHeight += this.header.getHeight();
17516         }
17517
17518         this.innerList = this.list.createChild({cls:cls+'-inner'});
17519         this.innerList.on('mouseover', this.onViewOver, this);
17520         this.innerList.on('mousemove', this.onViewMove, this);
17521         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17522         
17523         if(this.allowBlank && !this.pageSize && !this.disableClear){
17524             this.footer = this.list.createChild({cls:cls+'-ft'});
17525             this.pageTb = new Roo.Toolbar(this.footer);
17526            
17527         }
17528         if(this.pageSize){
17529             this.footer = this.list.createChild({cls:cls+'-ft'});
17530             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17531                     {pageSize: this.pageSize});
17532             
17533         }
17534         
17535         if (this.pageTb && this.allowBlank && !this.disableClear) {
17536             var _this = this;
17537             this.pageTb.add(new Roo.Toolbar.Fill(), {
17538                 cls: 'x-btn-icon x-btn-clear',
17539                 text: '&#160;',
17540                 handler: function()
17541                 {
17542                     _this.collapse();
17543                     _this.clearValue();
17544                     _this.onSelect(false, -1);
17545                 }
17546             });
17547         }
17548         if (this.footer) {
17549             this.assetHeight += this.footer.getHeight();
17550         }
17551         */
17552             
17553         if(!this.tpl){
17554             this.tpl = Roo.bootstrap.version == 4 ?
17555                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17556                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17557         }
17558
17559         this.view = new Roo.View(this.list, this.tpl, {
17560             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17561         });
17562         //this.view.wrapEl.setDisplayed(false);
17563         this.view.on('click', this.onViewClick, this);
17564         
17565         
17566         this.store.on('beforeload', this.onBeforeLoad, this);
17567         this.store.on('load', this.onLoad, this);
17568         this.store.on('loadexception', this.onLoadException, this);
17569         /*
17570         if(this.resizable){
17571             this.resizer = new Roo.Resizable(this.list,  {
17572                pinned:true, handles:'se'
17573             });
17574             this.resizer.on('resize', function(r, w, h){
17575                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17576                 this.listWidth = w;
17577                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17578                 this.restrictHeight();
17579             }, this);
17580             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17581         }
17582         */
17583         if(!this.editable){
17584             this.editable = true;
17585             this.setEditable(false);
17586         }
17587         
17588         /*
17589         
17590         if (typeof(this.events.add.listeners) != 'undefined') {
17591             
17592             this.addicon = this.wrap.createChild(
17593                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17594        
17595             this.addicon.on('click', function(e) {
17596                 this.fireEvent('add', this);
17597             }, this);
17598         }
17599         if (typeof(this.events.edit.listeners) != 'undefined') {
17600             
17601             this.editicon = this.wrap.createChild(
17602                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17603             if (this.addicon) {
17604                 this.editicon.setStyle('margin-left', '40px');
17605             }
17606             this.editicon.on('click', function(e) {
17607                 
17608                 // we fire even  if inothing is selected..
17609                 this.fireEvent('edit', this, this.lastData );
17610                 
17611             }, this);
17612         }
17613         */
17614         
17615         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17616             "up" : function(e){
17617                 this.inKeyMode = true;
17618                 this.selectPrev();
17619             },
17620
17621             "down" : function(e){
17622                 if(!this.isExpanded()){
17623                     this.onTriggerClick();
17624                 }else{
17625                     this.inKeyMode = true;
17626                     this.selectNext();
17627                 }
17628             },
17629
17630             "enter" : function(e){
17631 //                this.onViewClick();
17632                 //return true;
17633                 this.collapse();
17634                 
17635                 if(this.fireEvent("specialkey", this, e)){
17636                     this.onViewClick(false);
17637                 }
17638                 
17639                 return true;
17640             },
17641
17642             "esc" : function(e){
17643                 this.collapse();
17644             },
17645
17646             "tab" : function(e){
17647                 this.collapse();
17648                 
17649                 if(this.fireEvent("specialkey", this, e)){
17650                     this.onViewClick(false);
17651                 }
17652                 
17653                 return true;
17654             },
17655
17656             scope : this,
17657
17658             doRelay : function(foo, bar, hname){
17659                 if(hname == 'down' || this.scope.isExpanded()){
17660                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17661                 }
17662                 return true;
17663             },
17664
17665             forceKeyDown: true
17666         });
17667         
17668         
17669         this.queryDelay = Math.max(this.queryDelay || 10,
17670                 this.mode == 'local' ? 10 : 250);
17671         
17672         
17673         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17674         
17675         if(this.typeAhead){
17676             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17677         }
17678         if(this.editable !== false){
17679             this.inputEl().on("keyup", this.onKeyUp, this);
17680         }
17681         if(this.forceSelection){
17682             this.inputEl().on('blur', this.doForce, this);
17683         }
17684         
17685         if(this.multiple){
17686             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17687             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17688         }
17689     },
17690     
17691     initTickableEvents: function()
17692     {   
17693         this.createList();
17694         
17695         if(this.hiddenName){
17696             
17697             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17698             
17699             this.hiddenField.dom.value =
17700                 this.hiddenValue !== undefined ? this.hiddenValue :
17701                 this.value !== undefined ? this.value : '';
17702
17703             // prevent input submission
17704             this.el.dom.removeAttribute('name');
17705             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17706              
17707              
17708         }
17709         
17710 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17711         
17712         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17713         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17714         if(this.triggerList){
17715             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17716         }
17717          
17718         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17719         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17720         
17721         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17722         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17723         
17724         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17725         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17726         
17727         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17728         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17729         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17730         
17731         this.okBtn.hide();
17732         this.cancelBtn.hide();
17733         
17734         var _this = this;
17735         
17736         (function(){
17737             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17738             _this.list.setWidth(lw);
17739         }).defer(100);
17740         
17741         this.list.on('mouseover', this.onViewOver, this);
17742         this.list.on('mousemove', this.onViewMove, this);
17743         
17744         this.list.on('scroll', this.onViewScroll, this);
17745         
17746         if(!this.tpl){
17747             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17748                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17749         }
17750
17751         this.view = new Roo.View(this.list, this.tpl, {
17752             singleSelect:true,
17753             tickable:true,
17754             parent:this,
17755             store: this.store,
17756             selectedClass: this.selectedClass
17757         });
17758         
17759         //this.view.wrapEl.setDisplayed(false);
17760         this.view.on('click', this.onViewClick, this);
17761         
17762         
17763         
17764         this.store.on('beforeload', this.onBeforeLoad, this);
17765         this.store.on('load', this.onLoad, this);
17766         this.store.on('loadexception', this.onLoadException, this);
17767         
17768         if(this.editable){
17769             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17770                 "up" : function(e){
17771                     this.inKeyMode = true;
17772                     this.selectPrev();
17773                 },
17774
17775                 "down" : function(e){
17776                     this.inKeyMode = true;
17777                     this.selectNext();
17778                 },
17779
17780                 "enter" : function(e){
17781                     if(this.fireEvent("specialkey", this, e)){
17782                         this.onViewClick(false);
17783                     }
17784                     
17785                     return true;
17786                 },
17787
17788                 "esc" : function(e){
17789                     this.onTickableFooterButtonClick(e, false, false);
17790                 },
17791
17792                 "tab" : function(e){
17793                     this.fireEvent("specialkey", this, e);
17794                     
17795                     this.onTickableFooterButtonClick(e, false, false);
17796                     
17797                     return true;
17798                 },
17799
17800                 scope : this,
17801
17802                 doRelay : function(e, fn, key){
17803                     if(this.scope.isExpanded()){
17804                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17805                     }
17806                     return true;
17807                 },
17808
17809                 forceKeyDown: true
17810             });
17811         }
17812         
17813         this.queryDelay = Math.max(this.queryDelay || 10,
17814                 this.mode == 'local' ? 10 : 250);
17815         
17816         
17817         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17818         
17819         if(this.typeAhead){
17820             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17821         }
17822         
17823         if(this.editable !== false){
17824             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17825         }
17826         
17827         this.indicator = this.indicatorEl();
17828         
17829         if(this.indicator){
17830             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17831             this.indicator.hide();
17832         }
17833         
17834     },
17835
17836     onDestroy : function(){
17837         if(this.view){
17838             this.view.setStore(null);
17839             this.view.el.removeAllListeners();
17840             this.view.el.remove();
17841             this.view.purgeListeners();
17842         }
17843         if(this.list){
17844             this.list.dom.innerHTML  = '';
17845         }
17846         
17847         if(this.store){
17848             this.store.un('beforeload', this.onBeforeLoad, this);
17849             this.store.un('load', this.onLoad, this);
17850             this.store.un('loadexception', this.onLoadException, this);
17851         }
17852         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17853     },
17854
17855     // private
17856     fireKey : function(e){
17857         if(e.isNavKeyPress() && !this.list.isVisible()){
17858             this.fireEvent("specialkey", this, e);
17859         }
17860     },
17861
17862     // private
17863     onResize: function(w, h)
17864     {
17865         
17866         
17867 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17868 //        
17869 //        if(typeof w != 'number'){
17870 //            // we do not handle it!?!?
17871 //            return;
17872 //        }
17873 //        var tw = this.trigger.getWidth();
17874 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17875 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17876 //        var x = w - tw;
17877 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17878 //            
17879 //        //this.trigger.setStyle('left', x+'px');
17880 //        
17881 //        if(this.list && this.listWidth === undefined){
17882 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17883 //            this.list.setWidth(lw);
17884 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17885 //        }
17886         
17887     
17888         
17889     },
17890
17891     /**
17892      * Allow or prevent the user from directly editing the field text.  If false is passed,
17893      * the user will only be able to select from the items defined in the dropdown list.  This method
17894      * is the runtime equivalent of setting the 'editable' config option at config time.
17895      * @param {Boolean} value True to allow the user to directly edit the field text
17896      */
17897     setEditable : function(value){
17898         if(value == this.editable){
17899             return;
17900         }
17901         this.editable = value;
17902         if(!value){
17903             this.inputEl().dom.setAttribute('readOnly', true);
17904             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17905             this.inputEl().addClass('x-combo-noedit');
17906         }else{
17907             this.inputEl().dom.removeAttribute('readOnly');
17908             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17909             this.inputEl().removeClass('x-combo-noedit');
17910         }
17911     },
17912
17913     // private
17914     
17915     onBeforeLoad : function(combo,opts){
17916         if(!this.hasFocus){
17917             return;
17918         }
17919          if (!opts.add) {
17920             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17921          }
17922         this.restrictHeight();
17923         this.selectedIndex = -1;
17924     },
17925
17926     // private
17927     onLoad : function(){
17928         
17929         this.hasQuery = false;
17930         
17931         if(!this.hasFocus){
17932             return;
17933         }
17934         
17935         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17936             this.loading.hide();
17937         }
17938         
17939         if(this.store.getCount() > 0){
17940             
17941             this.expand();
17942             this.restrictHeight();
17943             if(this.lastQuery == this.allQuery){
17944                 if(this.editable && !this.tickable){
17945                     this.inputEl().dom.select();
17946                 }
17947                 
17948                 if(
17949                     !this.selectByValue(this.value, true) &&
17950                     this.autoFocus && 
17951                     (
17952                         !this.store.lastOptions ||
17953                         typeof(this.store.lastOptions.add) == 'undefined' || 
17954                         this.store.lastOptions.add != true
17955                     )
17956                 ){
17957                     this.select(0, true);
17958                 }
17959             }else{
17960                 if(this.autoFocus){
17961                     this.selectNext();
17962                 }
17963                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17964                     this.taTask.delay(this.typeAheadDelay);
17965                 }
17966             }
17967         }else{
17968             this.onEmptyResults();
17969         }
17970         
17971         //this.el.focus();
17972     },
17973     // private
17974     onLoadException : function()
17975     {
17976         this.hasQuery = false;
17977         
17978         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17979             this.loading.hide();
17980         }
17981         
17982         if(this.tickable && this.editable){
17983             return;
17984         }
17985         
17986         this.collapse();
17987         // only causes errors at present
17988         //Roo.log(this.store.reader.jsonData);
17989         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17990             // fixme
17991             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17992         //}
17993         
17994         
17995     },
17996     // private
17997     onTypeAhead : function(){
17998         if(this.store.getCount() > 0){
17999             var r = this.store.getAt(0);
18000             var newValue = r.data[this.displayField];
18001             var len = newValue.length;
18002             var selStart = this.getRawValue().length;
18003             
18004             if(selStart != len){
18005                 this.setRawValue(newValue);
18006                 this.selectText(selStart, newValue.length);
18007             }
18008         }
18009     },
18010
18011     // private
18012     onSelect : function(record, index){
18013         
18014         if(this.fireEvent('beforeselect', this, record, index) !== false){
18015         
18016             this.setFromData(index > -1 ? record.data : false);
18017             
18018             this.collapse();
18019             this.fireEvent('select', this, record, index);
18020         }
18021     },
18022
18023     /**
18024      * Returns the currently selected field value or empty string if no value is set.
18025      * @return {String} value The selected value
18026      */
18027     getValue : function()
18028     {
18029         if(Roo.isIOS && this.useNativeIOS){
18030             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18031         }
18032         
18033         if(this.multiple){
18034             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18035         }
18036         
18037         if(this.valueField){
18038             return typeof this.value != 'undefined' ? this.value : '';
18039         }else{
18040             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18041         }
18042     },
18043     
18044     getRawValue : function()
18045     {
18046         if(Roo.isIOS && this.useNativeIOS){
18047             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18048         }
18049         
18050         var v = this.inputEl().getValue();
18051         
18052         return v;
18053     },
18054
18055     /**
18056      * Clears any text/value currently set in the field
18057      */
18058     clearValue : function(){
18059         
18060         if(this.hiddenField){
18061             this.hiddenField.dom.value = '';
18062         }
18063         this.value = '';
18064         this.setRawValue('');
18065         this.lastSelectionText = '';
18066         this.lastData = false;
18067         
18068         var close = this.closeTriggerEl();
18069         
18070         if(close){
18071             close.hide();
18072         }
18073         
18074         this.validate();
18075         
18076     },
18077
18078     /**
18079      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18080      * will be displayed in the field.  If the value does not match the data value of an existing item,
18081      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18082      * Otherwise the field will be blank (although the value will still be set).
18083      * @param {String} value The value to match
18084      */
18085     setValue : function(v)
18086     {
18087         if(Roo.isIOS && this.useNativeIOS){
18088             this.setIOSValue(v);
18089             return;
18090         }
18091         
18092         if(this.multiple){
18093             this.syncValue();
18094             return;
18095         }
18096         
18097         var text = v;
18098         if(this.valueField){
18099             var r = this.findRecord(this.valueField, v);
18100             if(r){
18101                 text = r.data[this.displayField];
18102             }else if(this.valueNotFoundText !== undefined){
18103                 text = this.valueNotFoundText;
18104             }
18105         }
18106         this.lastSelectionText = text;
18107         if(this.hiddenField){
18108             this.hiddenField.dom.value = v;
18109         }
18110         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18111         this.value = v;
18112         
18113         var close = this.closeTriggerEl();
18114         
18115         if(close){
18116             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18117         }
18118         
18119         this.validate();
18120     },
18121     /**
18122      * @property {Object} the last set data for the element
18123      */
18124     
18125     lastData : false,
18126     /**
18127      * Sets the value of the field based on a object which is related to the record format for the store.
18128      * @param {Object} value the value to set as. or false on reset?
18129      */
18130     setFromData : function(o){
18131         
18132         if(this.multiple){
18133             this.addItem(o);
18134             return;
18135         }
18136             
18137         var dv = ''; // display value
18138         var vv = ''; // value value..
18139         this.lastData = o;
18140         if (this.displayField) {
18141             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18142         } else {
18143             // this is an error condition!!!
18144             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18145         }
18146         
18147         if(this.valueField){
18148             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18149         }
18150         
18151         var close = this.closeTriggerEl();
18152         
18153         if(close){
18154             if(dv.length || vv * 1 > 0){
18155                 close.show() ;
18156                 this.blockFocus=true;
18157             } else {
18158                 close.hide();
18159             }             
18160         }
18161         
18162         if(this.hiddenField){
18163             this.hiddenField.dom.value = vv;
18164             
18165             this.lastSelectionText = dv;
18166             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18167             this.value = vv;
18168             return;
18169         }
18170         // no hidden field.. - we store the value in 'value', but still display
18171         // display field!!!!
18172         this.lastSelectionText = dv;
18173         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18174         this.value = vv;
18175         
18176         
18177         
18178     },
18179     // private
18180     reset : function(){
18181         // overridden so that last data is reset..
18182         
18183         if(this.multiple){
18184             this.clearItem();
18185             return;
18186         }
18187         
18188         this.setValue(this.originalValue);
18189         //this.clearInvalid();
18190         this.lastData = false;
18191         if (this.view) {
18192             this.view.clearSelections();
18193         }
18194         
18195         this.validate();
18196     },
18197     // private
18198     findRecord : function(prop, value){
18199         var record;
18200         if(this.store.getCount() > 0){
18201             this.store.each(function(r){
18202                 if(r.data[prop] == value){
18203                     record = r;
18204                     return false;
18205                 }
18206                 return true;
18207             });
18208         }
18209         return record;
18210     },
18211     
18212     getName: function()
18213     {
18214         // returns hidden if it's set..
18215         if (!this.rendered) {return ''};
18216         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18217         
18218     },
18219     // private
18220     onViewMove : function(e, t){
18221         this.inKeyMode = false;
18222     },
18223
18224     // private
18225     onViewOver : function(e, t){
18226         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18227             return;
18228         }
18229         var item = this.view.findItemFromChild(t);
18230         
18231         if(item){
18232             var index = this.view.indexOf(item);
18233             this.select(index, false);
18234         }
18235     },
18236
18237     // private
18238     onViewClick : function(view, doFocus, el, e)
18239     {
18240         var index = this.view.getSelectedIndexes()[0];
18241         
18242         var r = this.store.getAt(index);
18243         
18244         if(this.tickable){
18245             
18246             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18247                 return;
18248             }
18249             
18250             var rm = false;
18251             var _this = this;
18252             
18253             Roo.each(this.tickItems, function(v,k){
18254                 
18255                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18256                     Roo.log(v);
18257                     _this.tickItems.splice(k, 1);
18258                     
18259                     if(typeof(e) == 'undefined' && view == false){
18260                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18261                     }
18262                     
18263                     rm = true;
18264                     return;
18265                 }
18266             });
18267             
18268             if(rm){
18269                 return;
18270             }
18271             
18272             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18273                 this.tickItems.push(r.data);
18274             }
18275             
18276             if(typeof(e) == 'undefined' && view == false){
18277                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18278             }
18279                     
18280             return;
18281         }
18282         
18283         if(r){
18284             this.onSelect(r, index);
18285         }
18286         if(doFocus !== false && !this.blockFocus){
18287             this.inputEl().focus();
18288         }
18289     },
18290
18291     // private
18292     restrictHeight : function(){
18293         //this.innerList.dom.style.height = '';
18294         //var inner = this.innerList.dom;
18295         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18296         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18297         //this.list.beginUpdate();
18298         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18299         this.list.alignTo(this.inputEl(), this.listAlign);
18300         this.list.alignTo(this.inputEl(), this.listAlign);
18301         //this.list.endUpdate();
18302     },
18303
18304     // private
18305     onEmptyResults : function(){
18306         
18307         if(this.tickable && this.editable){
18308             this.hasFocus = false;
18309             this.restrictHeight();
18310             return;
18311         }
18312         
18313         this.collapse();
18314     },
18315
18316     /**
18317      * Returns true if the dropdown list is expanded, else false.
18318      */
18319     isExpanded : function(){
18320         return this.list.isVisible();
18321     },
18322
18323     /**
18324      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18325      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18326      * @param {String} value The data value of the item to select
18327      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18328      * selected item if it is not currently in view (defaults to true)
18329      * @return {Boolean} True if the value matched an item in the list, else false
18330      */
18331     selectByValue : function(v, scrollIntoView){
18332         if(v !== undefined && v !== null){
18333             var r = this.findRecord(this.valueField || this.displayField, v);
18334             if(r){
18335                 this.select(this.store.indexOf(r), scrollIntoView);
18336                 return true;
18337             }
18338         }
18339         return false;
18340     },
18341
18342     /**
18343      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18344      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18345      * @param {Number} index The zero-based index of the list item to select
18346      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18347      * selected item if it is not currently in view (defaults to true)
18348      */
18349     select : function(index, scrollIntoView){
18350         this.selectedIndex = index;
18351         this.view.select(index);
18352         if(scrollIntoView !== false){
18353             var el = this.view.getNode(index);
18354             /*
18355              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18356              */
18357             if(el){
18358                 this.list.scrollChildIntoView(el, false);
18359             }
18360         }
18361     },
18362
18363     // private
18364     selectNext : function(){
18365         var ct = this.store.getCount();
18366         if(ct > 0){
18367             if(this.selectedIndex == -1){
18368                 this.select(0);
18369             }else if(this.selectedIndex < ct-1){
18370                 this.select(this.selectedIndex+1);
18371             }
18372         }
18373     },
18374
18375     // private
18376     selectPrev : function(){
18377         var ct = this.store.getCount();
18378         if(ct > 0){
18379             if(this.selectedIndex == -1){
18380                 this.select(0);
18381             }else if(this.selectedIndex != 0){
18382                 this.select(this.selectedIndex-1);
18383             }
18384         }
18385     },
18386
18387     // private
18388     onKeyUp : function(e){
18389         if(this.editable !== false && !e.isSpecialKey()){
18390             this.lastKey = e.getKey();
18391             this.dqTask.delay(this.queryDelay);
18392         }
18393     },
18394
18395     // private
18396     validateBlur : function(){
18397         return !this.list || !this.list.isVisible();   
18398     },
18399
18400     // private
18401     initQuery : function(){
18402         
18403         var v = this.getRawValue();
18404         
18405         if(this.tickable && this.editable){
18406             v = this.tickableInputEl().getValue();
18407         }
18408         
18409         this.doQuery(v);
18410     },
18411
18412     // private
18413     doForce : function(){
18414         if(this.inputEl().dom.value.length > 0){
18415             this.inputEl().dom.value =
18416                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18417              
18418         }
18419     },
18420
18421     /**
18422      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18423      * query allowing the query action to be canceled if needed.
18424      * @param {String} query The SQL query to execute
18425      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18426      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18427      * saved in the current store (defaults to false)
18428      */
18429     doQuery : function(q, forceAll){
18430         
18431         if(q === undefined || q === null){
18432             q = '';
18433         }
18434         var qe = {
18435             query: q,
18436             forceAll: forceAll,
18437             combo: this,
18438             cancel:false
18439         };
18440         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18441             return false;
18442         }
18443         q = qe.query;
18444         
18445         forceAll = qe.forceAll;
18446         if(forceAll === true || (q.length >= this.minChars)){
18447             
18448             this.hasQuery = true;
18449             
18450             if(this.lastQuery != q || this.alwaysQuery){
18451                 this.lastQuery = q;
18452                 if(this.mode == 'local'){
18453                     this.selectedIndex = -1;
18454                     if(forceAll){
18455                         this.store.clearFilter();
18456                     }else{
18457                         
18458                         if(this.specialFilter){
18459                             this.fireEvent('specialfilter', this);
18460                             this.onLoad();
18461                             return;
18462                         }
18463                         
18464                         this.store.filter(this.displayField, q);
18465                     }
18466                     
18467                     this.store.fireEvent("datachanged", this.store);
18468                     
18469                     this.onLoad();
18470                     
18471                     
18472                 }else{
18473                     
18474                     this.store.baseParams[this.queryParam] = q;
18475                     
18476                     var options = {params : this.getParams(q)};
18477                     
18478                     if(this.loadNext){
18479                         options.add = true;
18480                         options.params.start = this.page * this.pageSize;
18481                     }
18482                     
18483                     this.store.load(options);
18484                     
18485                     /*
18486                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18487                      *  we should expand the list on onLoad
18488                      *  so command out it
18489                      */
18490 //                    this.expand();
18491                 }
18492             }else{
18493                 this.selectedIndex = -1;
18494                 this.onLoad();   
18495             }
18496         }
18497         
18498         this.loadNext = false;
18499     },
18500     
18501     // private
18502     getParams : function(q){
18503         var p = {};
18504         //p[this.queryParam] = q;
18505         
18506         if(this.pageSize){
18507             p.start = 0;
18508             p.limit = this.pageSize;
18509         }
18510         return p;
18511     },
18512
18513     /**
18514      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18515      */
18516     collapse : function(){
18517         if(!this.isExpanded()){
18518             return;
18519         }
18520         
18521         this.list.hide();
18522         
18523         this.hasFocus = false;
18524         
18525         if(this.tickable){
18526             this.okBtn.hide();
18527             this.cancelBtn.hide();
18528             this.trigger.show();
18529             
18530             if(this.editable){
18531                 this.tickableInputEl().dom.value = '';
18532                 this.tickableInputEl().blur();
18533             }
18534             
18535         }
18536         
18537         Roo.get(document).un('mousedown', this.collapseIf, this);
18538         Roo.get(document).un('mousewheel', this.collapseIf, this);
18539         if (!this.editable) {
18540             Roo.get(document).un('keydown', this.listKeyPress, this);
18541         }
18542         this.fireEvent('collapse', this);
18543         
18544         this.validate();
18545     },
18546
18547     // private
18548     collapseIf : function(e){
18549         var in_combo  = e.within(this.el);
18550         var in_list =  e.within(this.list);
18551         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18552         
18553         if (in_combo || in_list || is_list) {
18554             //e.stopPropagation();
18555             return;
18556         }
18557         
18558         if(this.tickable){
18559             this.onTickableFooterButtonClick(e, false, false);
18560         }
18561
18562         this.collapse();
18563         
18564     },
18565
18566     /**
18567      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18568      */
18569     expand : function(){
18570        
18571         if(this.isExpanded() || !this.hasFocus){
18572             return;
18573         }
18574         
18575         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18576         this.list.setWidth(lw);
18577         
18578         Roo.log('expand');
18579         
18580         this.list.show();
18581         
18582         this.restrictHeight();
18583         
18584         if(this.tickable){
18585             
18586             this.tickItems = Roo.apply([], this.item);
18587             
18588             this.okBtn.show();
18589             this.cancelBtn.show();
18590             this.trigger.hide();
18591             
18592             if(this.editable){
18593                 this.tickableInputEl().focus();
18594             }
18595             
18596         }
18597         
18598         Roo.get(document).on('mousedown', this.collapseIf, this);
18599         Roo.get(document).on('mousewheel', this.collapseIf, this);
18600         if (!this.editable) {
18601             Roo.get(document).on('keydown', this.listKeyPress, this);
18602         }
18603         
18604         this.fireEvent('expand', this);
18605     },
18606
18607     // private
18608     // Implements the default empty TriggerField.onTriggerClick function
18609     onTriggerClick : function(e)
18610     {
18611         Roo.log('trigger click');
18612         
18613         if(this.disabled || !this.triggerList){
18614             return;
18615         }
18616         
18617         this.page = 0;
18618         this.loadNext = false;
18619         
18620         if(this.isExpanded()){
18621             this.collapse();
18622             if (!this.blockFocus) {
18623                 this.inputEl().focus();
18624             }
18625             
18626         }else {
18627             this.hasFocus = true;
18628             if(this.triggerAction == 'all') {
18629                 this.doQuery(this.allQuery, true);
18630             } else {
18631                 this.doQuery(this.getRawValue());
18632             }
18633             if (!this.blockFocus) {
18634                 this.inputEl().focus();
18635             }
18636         }
18637     },
18638     
18639     onTickableTriggerClick : function(e)
18640     {
18641         if(this.disabled){
18642             return;
18643         }
18644         
18645         this.page = 0;
18646         this.loadNext = false;
18647         this.hasFocus = true;
18648         
18649         if(this.triggerAction == 'all') {
18650             this.doQuery(this.allQuery, true);
18651         } else {
18652             this.doQuery(this.getRawValue());
18653         }
18654     },
18655     
18656     onSearchFieldClick : function(e)
18657     {
18658         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18659             this.onTickableFooterButtonClick(e, false, false);
18660             return;
18661         }
18662         
18663         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18664             return;
18665         }
18666         
18667         this.page = 0;
18668         this.loadNext = false;
18669         this.hasFocus = true;
18670         
18671         if(this.triggerAction == 'all') {
18672             this.doQuery(this.allQuery, true);
18673         } else {
18674             this.doQuery(this.getRawValue());
18675         }
18676     },
18677     
18678     listKeyPress : function(e)
18679     {
18680         //Roo.log('listkeypress');
18681         // scroll to first matching element based on key pres..
18682         if (e.isSpecialKey()) {
18683             return false;
18684         }
18685         var k = String.fromCharCode(e.getKey()).toUpperCase();
18686         //Roo.log(k);
18687         var match  = false;
18688         var csel = this.view.getSelectedNodes();
18689         var cselitem = false;
18690         if (csel.length) {
18691             var ix = this.view.indexOf(csel[0]);
18692             cselitem  = this.store.getAt(ix);
18693             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18694                 cselitem = false;
18695             }
18696             
18697         }
18698         
18699         this.store.each(function(v) { 
18700             if (cselitem) {
18701                 // start at existing selection.
18702                 if (cselitem.id == v.id) {
18703                     cselitem = false;
18704                 }
18705                 return true;
18706             }
18707                 
18708             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18709                 match = this.store.indexOf(v);
18710                 return false;
18711             }
18712             return true;
18713         }, this);
18714         
18715         if (match === false) {
18716             return true; // no more action?
18717         }
18718         // scroll to?
18719         this.view.select(match);
18720         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18721         sn.scrollIntoView(sn.dom.parentNode, false);
18722     },
18723     
18724     onViewScroll : function(e, t){
18725         
18726         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
18727             return;
18728         }
18729         
18730         this.hasQuery = true;
18731         
18732         this.loading = this.list.select('.loading', true).first();
18733         
18734         if(this.loading === null){
18735             this.list.createChild({
18736                 tag: 'div',
18737                 cls: 'loading roo-select2-more-results roo-select2-active',
18738                 html: 'Loading more results...'
18739             });
18740             
18741             this.loading = this.list.select('.loading', true).first();
18742             
18743             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18744             
18745             this.loading.hide();
18746         }
18747         
18748         this.loading.show();
18749         
18750         var _combo = this;
18751         
18752         this.page++;
18753         this.loadNext = true;
18754         
18755         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18756         
18757         return;
18758     },
18759     
18760     addItem : function(o)
18761     {   
18762         var dv = ''; // display value
18763         
18764         if (this.displayField) {
18765             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18766         } else {
18767             // this is an error condition!!!
18768             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18769         }
18770         
18771         if(!dv.length){
18772             return;
18773         }
18774         
18775         var choice = this.choices.createChild({
18776             tag: 'li',
18777             cls: 'roo-select2-search-choice',
18778             cn: [
18779                 {
18780                     tag: 'div',
18781                     html: dv
18782                 },
18783                 {
18784                     tag: 'a',
18785                     href: '#',
18786                     cls: 'roo-select2-search-choice-close fa fa-times',
18787                     tabindex: '-1'
18788                 }
18789             ]
18790             
18791         }, this.searchField);
18792         
18793         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18794         
18795         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18796         
18797         this.item.push(o);
18798         
18799         this.lastData = o;
18800         
18801         this.syncValue();
18802         
18803         this.inputEl().dom.value = '';
18804         
18805         this.validate();
18806     },
18807     
18808     onRemoveItem : function(e, _self, o)
18809     {
18810         e.preventDefault();
18811         
18812         this.lastItem = Roo.apply([], this.item);
18813         
18814         var index = this.item.indexOf(o.data) * 1;
18815         
18816         if( index < 0){
18817             Roo.log('not this item?!');
18818             return;
18819         }
18820         
18821         this.item.splice(index, 1);
18822         o.item.remove();
18823         
18824         this.syncValue();
18825         
18826         this.fireEvent('remove', this, e);
18827         
18828         this.validate();
18829         
18830     },
18831     
18832     syncValue : function()
18833     {
18834         if(!this.item.length){
18835             this.clearValue();
18836             return;
18837         }
18838             
18839         var value = [];
18840         var _this = this;
18841         Roo.each(this.item, function(i){
18842             if(_this.valueField){
18843                 value.push(i[_this.valueField]);
18844                 return;
18845             }
18846
18847             value.push(i);
18848         });
18849
18850         this.value = value.join(',');
18851
18852         if(this.hiddenField){
18853             this.hiddenField.dom.value = this.value;
18854         }
18855         
18856         this.store.fireEvent("datachanged", this.store);
18857         
18858         this.validate();
18859     },
18860     
18861     clearItem : function()
18862     {
18863         if(!this.multiple){
18864             return;
18865         }
18866         
18867         this.item = [];
18868         
18869         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18870            c.remove();
18871         });
18872         
18873         this.syncValue();
18874         
18875         this.validate();
18876         
18877         if(this.tickable && !Roo.isTouch){
18878             this.view.refresh();
18879         }
18880     },
18881     
18882     inputEl: function ()
18883     {
18884         if(Roo.isIOS && this.useNativeIOS){
18885             return this.el.select('select.roo-ios-select', true).first();
18886         }
18887         
18888         if(Roo.isTouch && this.mobileTouchView){
18889             return this.el.select('input.form-control',true).first();
18890         }
18891         
18892         if(this.tickable){
18893             return this.searchField;
18894         }
18895         
18896         return this.el.select('input.form-control',true).first();
18897     },
18898     
18899     onTickableFooterButtonClick : function(e, btn, el)
18900     {
18901         e.preventDefault();
18902         
18903         this.lastItem = Roo.apply([], this.item);
18904         
18905         if(btn && btn.name == 'cancel'){
18906             this.tickItems = Roo.apply([], this.item);
18907             this.collapse();
18908             return;
18909         }
18910         
18911         this.clearItem();
18912         
18913         var _this = this;
18914         
18915         Roo.each(this.tickItems, function(o){
18916             _this.addItem(o);
18917         });
18918         
18919         this.collapse();
18920         
18921     },
18922     
18923     validate : function()
18924     {
18925         if(this.getVisibilityEl().hasClass('hidden')){
18926             return true;
18927         }
18928         
18929         var v = this.getRawValue();
18930         
18931         if(this.multiple){
18932             v = this.getValue();
18933         }
18934         
18935         if(this.disabled || this.allowBlank || v.length){
18936             this.markValid();
18937             return true;
18938         }
18939         
18940         this.markInvalid();
18941         return false;
18942     },
18943     
18944     tickableInputEl : function()
18945     {
18946         if(!this.tickable || !this.editable){
18947             return this.inputEl();
18948         }
18949         
18950         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18951     },
18952     
18953     
18954     getAutoCreateTouchView : function()
18955     {
18956         var id = Roo.id();
18957         
18958         var cfg = {
18959             cls: 'form-group' //input-group
18960         };
18961         
18962         var input =  {
18963             tag: 'input',
18964             id : id,
18965             type : this.inputType,
18966             cls : 'form-control x-combo-noedit',
18967             autocomplete: 'new-password',
18968             placeholder : this.placeholder || '',
18969             readonly : true
18970         };
18971         
18972         if (this.name) {
18973             input.name = this.name;
18974         }
18975         
18976         if (this.size) {
18977             input.cls += ' input-' + this.size;
18978         }
18979         
18980         if (this.disabled) {
18981             input.disabled = true;
18982         }
18983         
18984         var inputblock = {
18985             cls : 'roo-combobox-wrap',
18986             cn : [
18987                 input
18988             ]
18989         };
18990         
18991         if(this.before){
18992             inputblock.cls += ' input-group';
18993             
18994             inputblock.cn.unshift({
18995                 tag :'span',
18996                 cls : 'input-group-addon input-group-prepend input-group-text',
18997                 html : this.before
18998             });
18999         }
19000         
19001         if(this.removable && !this.multiple){
19002             inputblock.cls += ' roo-removable';
19003             
19004             inputblock.cn.push({
19005                 tag: 'button',
19006                 html : 'x',
19007                 cls : 'roo-combo-removable-btn close'
19008             });
19009         }
19010
19011         if(this.hasFeedback && !this.allowBlank){
19012             
19013             inputblock.cls += ' has-feedback';
19014             
19015             inputblock.cn.push({
19016                 tag: 'span',
19017                 cls: 'glyphicon form-control-feedback'
19018             });
19019             
19020         }
19021         
19022         if (this.after) {
19023             
19024             inputblock.cls += (this.before) ? '' : ' input-group';
19025             
19026             inputblock.cn.push({
19027                 tag :'span',
19028                 cls : 'input-group-addon input-group-append input-group-text',
19029                 html : this.after
19030             });
19031         }
19032
19033         
19034         var ibwrap = inputblock;
19035         
19036         if(this.multiple){
19037             ibwrap = {
19038                 tag: 'ul',
19039                 cls: 'roo-select2-choices',
19040                 cn:[
19041                     {
19042                         tag: 'li',
19043                         cls: 'roo-select2-search-field',
19044                         cn: [
19045
19046                             inputblock
19047                         ]
19048                     }
19049                 ]
19050             };
19051         
19052             
19053         }
19054         
19055         var combobox = {
19056             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19057             cn: [
19058                 {
19059                     tag: 'input',
19060                     type : 'hidden',
19061                     cls: 'form-hidden-field'
19062                 },
19063                 ibwrap
19064             ]
19065         };
19066         
19067         if(!this.multiple && this.showToggleBtn){
19068             
19069             var caret = {
19070                 cls: 'caret'
19071             };
19072             
19073             if (this.caret != false) {
19074                 caret = {
19075                      tag: 'i',
19076                      cls: 'fa fa-' + this.caret
19077                 };
19078                 
19079             }
19080             
19081             combobox.cn.push({
19082                 tag :'span',
19083                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19084                 cn : [
19085                     Roo.bootstrap.version == 3 ? caret : '',
19086                     {
19087                         tag: 'span',
19088                         cls: 'combobox-clear',
19089                         cn  : [
19090                             {
19091                                 tag : 'i',
19092                                 cls: 'icon-remove'
19093                             }
19094                         ]
19095                     }
19096                 ]
19097
19098             })
19099         }
19100         
19101         if(this.multiple){
19102             combobox.cls += ' roo-select2-container-multi';
19103         }
19104         
19105         var required =  this.allowBlank ?  {
19106                     tag : 'i',
19107                     style: 'display: none'
19108                 } : {
19109                    tag : 'i',
19110                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19111                    tooltip : 'This field is required'
19112                 };
19113         
19114         var align = this.labelAlign || this.parentLabelAlign();
19115         
19116         if (align ==='left' && this.fieldLabel.length) {
19117
19118             cfg.cn = [
19119                 required,
19120                 {
19121                     tag: 'label',
19122                     cls : 'control-label col-form-label',
19123                     html : this.fieldLabel
19124
19125                 },
19126                 {
19127                     cls : 'roo-combobox-wrap ', 
19128                     cn: [
19129                         combobox
19130                     ]
19131                 }
19132             ];
19133             
19134             var labelCfg = cfg.cn[1];
19135             var contentCfg = cfg.cn[2];
19136             
19137
19138             if(this.indicatorpos == 'right'){
19139                 cfg.cn = [
19140                     {
19141                         tag: 'label',
19142                         'for' :  id,
19143                         cls : 'control-label col-form-label',
19144                         cn : [
19145                             {
19146                                 tag : 'span',
19147                                 html : this.fieldLabel
19148                             },
19149                             required
19150                         ]
19151                     },
19152                     {
19153                         cls : "roo-combobox-wrap ",
19154                         cn: [
19155                             combobox
19156                         ]
19157                     }
19158
19159                 ];
19160                 
19161                 labelCfg = cfg.cn[0];
19162                 contentCfg = cfg.cn[1];
19163             }
19164             
19165            
19166             
19167             if(this.labelWidth > 12){
19168                 labelCfg.style = "width: " + this.labelWidth + 'px';
19169             }
19170            
19171             if(this.labelWidth < 13 && this.labelmd == 0){
19172                 this.labelmd = this.labelWidth;
19173             }
19174             
19175             if(this.labellg > 0){
19176                 labelCfg.cls += ' col-lg-' + this.labellg;
19177                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19178             }
19179             
19180             if(this.labelmd > 0){
19181                 labelCfg.cls += ' col-md-' + this.labelmd;
19182                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19183             }
19184             
19185             if(this.labelsm > 0){
19186                 labelCfg.cls += ' col-sm-' + this.labelsm;
19187                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19188             }
19189             
19190             if(this.labelxs > 0){
19191                 labelCfg.cls += ' col-xs-' + this.labelxs;
19192                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19193             }
19194                 
19195                 
19196         } else if ( this.fieldLabel.length) {
19197             cfg.cn = [
19198                required,
19199                 {
19200                     tag: 'label',
19201                     cls : 'control-label',
19202                     html : this.fieldLabel
19203
19204                 },
19205                 {
19206                     cls : '', 
19207                     cn: [
19208                         combobox
19209                     ]
19210                 }
19211             ];
19212             
19213             if(this.indicatorpos == 'right'){
19214                 cfg.cn = [
19215                     {
19216                         tag: 'label',
19217                         cls : 'control-label',
19218                         html : this.fieldLabel,
19219                         cn : [
19220                             required
19221                         ]
19222                     },
19223                     {
19224                         cls : '', 
19225                         cn: [
19226                             combobox
19227                         ]
19228                     }
19229                 ];
19230             }
19231         } else {
19232             cfg.cn = combobox;    
19233         }
19234         
19235         
19236         var settings = this;
19237         
19238         ['xs','sm','md','lg'].map(function(size){
19239             if (settings[size]) {
19240                 cfg.cls += ' col-' + size + '-' + settings[size];
19241             }
19242         });
19243         
19244         return cfg;
19245     },
19246     
19247     initTouchView : function()
19248     {
19249         this.renderTouchView();
19250         
19251         this.touchViewEl.on('scroll', function(){
19252             this.el.dom.scrollTop = 0;
19253         }, this);
19254         
19255         this.originalValue = this.getValue();
19256         
19257         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19258         
19259         this.inputEl().on("click", this.showTouchView, this);
19260         if (this.triggerEl) {
19261             this.triggerEl.on("click", this.showTouchView, this);
19262         }
19263         
19264         
19265         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19266         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19267         
19268         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19269         
19270         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19271         this.store.on('load', this.onTouchViewLoad, this);
19272         this.store.on('loadexception', this.onTouchViewLoadException, this);
19273         
19274         if(this.hiddenName){
19275             
19276             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19277             
19278             this.hiddenField.dom.value =
19279                 this.hiddenValue !== undefined ? this.hiddenValue :
19280                 this.value !== undefined ? this.value : '';
19281         
19282             this.el.dom.removeAttribute('name');
19283             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19284         }
19285         
19286         if(this.multiple){
19287             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19288             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19289         }
19290         
19291         if(this.removable && !this.multiple){
19292             var close = this.closeTriggerEl();
19293             if(close){
19294                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19295                 close.on('click', this.removeBtnClick, this, close);
19296             }
19297         }
19298         /*
19299          * fix the bug in Safari iOS8
19300          */
19301         this.inputEl().on("focus", function(e){
19302             document.activeElement.blur();
19303         }, this);
19304         
19305         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19306         
19307         return;
19308         
19309         
19310     },
19311     
19312     renderTouchView : function()
19313     {
19314         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19315         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19316         
19317         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19318         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19319         
19320         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19321         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19322         this.touchViewBodyEl.setStyle('overflow', 'auto');
19323         
19324         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19325         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19326         
19327         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19328         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19329         
19330     },
19331     
19332     showTouchView : function()
19333     {
19334         if(this.disabled){
19335             return;
19336         }
19337         
19338         this.touchViewHeaderEl.hide();
19339
19340         if(this.modalTitle.length){
19341             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19342             this.touchViewHeaderEl.show();
19343         }
19344
19345         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19346         this.touchViewEl.show();
19347
19348         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19349         
19350         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19351         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19352
19353         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19354
19355         if(this.modalTitle.length){
19356             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19357         }
19358         
19359         this.touchViewBodyEl.setHeight(bodyHeight);
19360
19361         if(this.animate){
19362             var _this = this;
19363             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19364         }else{
19365             this.touchViewEl.addClass(['in','show']);
19366         }
19367         
19368         if(this._touchViewMask){
19369             Roo.get(document.body).addClass("x-body-masked");
19370             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19371             this._touchViewMask.setStyle('z-index', 10000);
19372             this._touchViewMask.addClass('show');
19373         }
19374         
19375         this.doTouchViewQuery();
19376         
19377     },
19378     
19379     hideTouchView : function()
19380     {
19381         this.touchViewEl.removeClass(['in','show']);
19382
19383         if(this.animate){
19384             var _this = this;
19385             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19386         }else{
19387             this.touchViewEl.setStyle('display', 'none');
19388         }
19389         
19390         if(this._touchViewMask){
19391             this._touchViewMask.removeClass('show');
19392             Roo.get(document.body).removeClass("x-body-masked");
19393         }
19394     },
19395     
19396     setTouchViewValue : function()
19397     {
19398         if(this.multiple){
19399             this.clearItem();
19400         
19401             var _this = this;
19402
19403             Roo.each(this.tickItems, function(o){
19404                 this.addItem(o);
19405             }, this);
19406         }
19407         
19408         this.hideTouchView();
19409     },
19410     
19411     doTouchViewQuery : function()
19412     {
19413         var qe = {
19414             query: '',
19415             forceAll: true,
19416             combo: this,
19417             cancel:false
19418         };
19419         
19420         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19421             return false;
19422         }
19423         
19424         if(!this.alwaysQuery || this.mode == 'local'){
19425             this.onTouchViewLoad();
19426             return;
19427         }
19428         
19429         this.store.load();
19430     },
19431     
19432     onTouchViewBeforeLoad : function(combo,opts)
19433     {
19434         return;
19435     },
19436
19437     // private
19438     onTouchViewLoad : function()
19439     {
19440         if(this.store.getCount() < 1){
19441             this.onTouchViewEmptyResults();
19442             return;
19443         }
19444         
19445         this.clearTouchView();
19446         
19447         var rawValue = this.getRawValue();
19448         
19449         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19450         
19451         this.tickItems = [];
19452         
19453         this.store.data.each(function(d, rowIndex){
19454             var row = this.touchViewListGroup.createChild(template);
19455             
19456             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19457                 row.addClass(d.data.cls);
19458             }
19459             
19460             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19461                 var cfg = {
19462                     data : d.data,
19463                     html : d.data[this.displayField]
19464                 };
19465                 
19466                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19467                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19468                 }
19469             }
19470             row.removeClass('selected');
19471             if(!this.multiple && this.valueField &&
19472                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19473             {
19474                 // radio buttons..
19475                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19476                 row.addClass('selected');
19477             }
19478             
19479             if(this.multiple && this.valueField &&
19480                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19481             {
19482                 
19483                 // checkboxes...
19484                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19485                 this.tickItems.push(d.data);
19486             }
19487             
19488             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19489             
19490         }, this);
19491         
19492         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19493         
19494         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19495
19496         if(this.modalTitle.length){
19497             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19498         }
19499
19500         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19501         
19502         if(this.mobile_restrict_height && listHeight < bodyHeight){
19503             this.touchViewBodyEl.setHeight(listHeight);
19504         }
19505         
19506         var _this = this;
19507         
19508         if(firstChecked && listHeight > bodyHeight){
19509             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19510         }
19511         
19512     },
19513     
19514     onTouchViewLoadException : function()
19515     {
19516         this.hideTouchView();
19517     },
19518     
19519     onTouchViewEmptyResults : function()
19520     {
19521         this.clearTouchView();
19522         
19523         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19524         
19525         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19526         
19527     },
19528     
19529     clearTouchView : function()
19530     {
19531         this.touchViewListGroup.dom.innerHTML = '';
19532     },
19533     
19534     onTouchViewClick : function(e, el, o)
19535     {
19536         e.preventDefault();
19537         
19538         var row = o.row;
19539         var rowIndex = o.rowIndex;
19540         
19541         var r = this.store.getAt(rowIndex);
19542         
19543         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19544             
19545             if(!this.multiple){
19546                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19547                     c.dom.removeAttribute('checked');
19548                 }, this);
19549
19550                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19551
19552                 this.setFromData(r.data);
19553
19554                 var close = this.closeTriggerEl();
19555
19556                 if(close){
19557                     close.show();
19558                 }
19559
19560                 this.hideTouchView();
19561
19562                 this.fireEvent('select', this, r, rowIndex);
19563
19564                 return;
19565             }
19566
19567             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19568                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19569                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19570                 return;
19571             }
19572
19573             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19574             this.addItem(r.data);
19575             this.tickItems.push(r.data);
19576         }
19577     },
19578     
19579     getAutoCreateNativeIOS : function()
19580     {
19581         var cfg = {
19582             cls: 'form-group' //input-group,
19583         };
19584         
19585         var combobox =  {
19586             tag: 'select',
19587             cls : 'roo-ios-select'
19588         };
19589         
19590         if (this.name) {
19591             combobox.name = this.name;
19592         }
19593         
19594         if (this.disabled) {
19595             combobox.disabled = true;
19596         }
19597         
19598         var settings = this;
19599         
19600         ['xs','sm','md','lg'].map(function(size){
19601             if (settings[size]) {
19602                 cfg.cls += ' col-' + size + '-' + settings[size];
19603             }
19604         });
19605         
19606         cfg.cn = combobox;
19607         
19608         return cfg;
19609         
19610     },
19611     
19612     initIOSView : function()
19613     {
19614         this.store.on('load', this.onIOSViewLoad, this);
19615         
19616         return;
19617     },
19618     
19619     onIOSViewLoad : function()
19620     {
19621         if(this.store.getCount() < 1){
19622             return;
19623         }
19624         
19625         this.clearIOSView();
19626         
19627         if(this.allowBlank) {
19628             
19629             var default_text = '-- SELECT --';
19630             
19631             if(this.placeholder.length){
19632                 default_text = this.placeholder;
19633             }
19634             
19635             if(this.emptyTitle.length){
19636                 default_text += ' - ' + this.emptyTitle + ' -';
19637             }
19638             
19639             var opt = this.inputEl().createChild({
19640                 tag: 'option',
19641                 value : 0,
19642                 html : default_text
19643             });
19644             
19645             var o = {};
19646             o[this.valueField] = 0;
19647             o[this.displayField] = default_text;
19648             
19649             this.ios_options.push({
19650                 data : o,
19651                 el : opt
19652             });
19653             
19654         }
19655         
19656         this.store.data.each(function(d, rowIndex){
19657             
19658             var html = '';
19659             
19660             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19661                 html = d.data[this.displayField];
19662             }
19663             
19664             var value = '';
19665             
19666             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19667                 value = d.data[this.valueField];
19668             }
19669             
19670             var option = {
19671                 tag: 'option',
19672                 value : value,
19673                 html : html
19674             };
19675             
19676             if(this.value == d.data[this.valueField]){
19677                 option['selected'] = true;
19678             }
19679             
19680             var opt = this.inputEl().createChild(option);
19681             
19682             this.ios_options.push({
19683                 data : d.data,
19684                 el : opt
19685             });
19686             
19687         }, this);
19688         
19689         this.inputEl().on('change', function(){
19690            this.fireEvent('select', this);
19691         }, this);
19692         
19693     },
19694     
19695     clearIOSView: function()
19696     {
19697         this.inputEl().dom.innerHTML = '';
19698         
19699         this.ios_options = [];
19700     },
19701     
19702     setIOSValue: function(v)
19703     {
19704         this.value = v;
19705         
19706         if(!this.ios_options){
19707             return;
19708         }
19709         
19710         Roo.each(this.ios_options, function(opts){
19711            
19712            opts.el.dom.removeAttribute('selected');
19713            
19714            if(opts.data[this.valueField] != v){
19715                return;
19716            }
19717            
19718            opts.el.dom.setAttribute('selected', true);
19719            
19720         }, this);
19721     }
19722
19723     /** 
19724     * @cfg {Boolean} grow 
19725     * @hide 
19726     */
19727     /** 
19728     * @cfg {Number} growMin 
19729     * @hide 
19730     */
19731     /** 
19732     * @cfg {Number} growMax 
19733     * @hide 
19734     */
19735     /**
19736      * @hide
19737      * @method autoSize
19738      */
19739 });
19740
19741 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19742     
19743     header : {
19744         tag: 'div',
19745         cls: 'modal-header',
19746         cn: [
19747             {
19748                 tag: 'h4',
19749                 cls: 'modal-title'
19750             }
19751         ]
19752     },
19753     
19754     body : {
19755         tag: 'div',
19756         cls: 'modal-body',
19757         cn: [
19758             {
19759                 tag: 'ul',
19760                 cls: 'list-group'
19761             }
19762         ]
19763     },
19764     
19765     listItemRadio : {
19766         tag: 'li',
19767         cls: 'list-group-item',
19768         cn: [
19769             {
19770                 tag: 'span',
19771                 cls: 'roo-combobox-list-group-item-value'
19772             },
19773             {
19774                 tag: 'div',
19775                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19776                 cn: [
19777                     {
19778                         tag: 'input',
19779                         type: 'radio'
19780                     },
19781                     {
19782                         tag: 'label'
19783                     }
19784                 ]
19785             }
19786         ]
19787     },
19788     
19789     listItemCheckbox : {
19790         tag: 'li',
19791         cls: 'list-group-item',
19792         cn: [
19793             {
19794                 tag: 'span',
19795                 cls: 'roo-combobox-list-group-item-value'
19796             },
19797             {
19798                 tag: 'div',
19799                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19800                 cn: [
19801                     {
19802                         tag: 'input',
19803                         type: 'checkbox'
19804                     },
19805                     {
19806                         tag: 'label'
19807                     }
19808                 ]
19809             }
19810         ]
19811     },
19812     
19813     emptyResult : {
19814         tag: 'div',
19815         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19816     },
19817     
19818     footer : {
19819         tag: 'div',
19820         cls: 'modal-footer',
19821         cn: [
19822             {
19823                 tag: 'div',
19824                 cls: 'row',
19825                 cn: [
19826                     {
19827                         tag: 'div',
19828                         cls: 'col-xs-6 text-left',
19829                         cn: {
19830                             tag: 'button',
19831                             cls: 'btn btn-danger roo-touch-view-cancel',
19832                             html: 'Cancel'
19833                         }
19834                     },
19835                     {
19836                         tag: 'div',
19837                         cls: 'col-xs-6 text-right',
19838                         cn: {
19839                             tag: 'button',
19840                             cls: 'btn btn-success roo-touch-view-ok',
19841                             html: 'OK'
19842                         }
19843                     }
19844                 ]
19845             }
19846         ]
19847         
19848     }
19849 });
19850
19851 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19852     
19853     touchViewTemplate : {
19854         tag: 'div',
19855         cls: 'modal fade roo-combobox-touch-view',
19856         cn: [
19857             {
19858                 tag: 'div',
19859                 cls: 'modal-dialog',
19860                 style : 'position:fixed', // we have to fix position....
19861                 cn: [
19862                     {
19863                         tag: 'div',
19864                         cls: 'modal-content',
19865                         cn: [
19866                             Roo.bootstrap.form.ComboBox.header,
19867                             Roo.bootstrap.form.ComboBox.body,
19868                             Roo.bootstrap.form.ComboBox.footer
19869                         ]
19870                     }
19871                 ]
19872             }
19873         ]
19874     }
19875 });/*
19876  * Based on:
19877  * Ext JS Library 1.1.1
19878  * Copyright(c) 2006-2007, Ext JS, LLC.
19879  *
19880  * Originally Released Under LGPL - original licence link has changed is not relivant.
19881  *
19882  * Fork - LGPL
19883  * <script type="text/javascript">
19884  */
19885
19886 /**
19887  * @class Roo.View
19888  * @extends Roo.util.Observable
19889  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19890  * This class also supports single and multi selection modes. <br>
19891  * Create a data model bound view:
19892  <pre><code>
19893  var store = new Roo.data.Store(...);
19894
19895  var view = new Roo.View({
19896     el : "my-element",
19897     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19898  
19899     singleSelect: true,
19900     selectedClass: "ydataview-selected",
19901     store: store
19902  });
19903
19904  // listen for node click?
19905  view.on("click", function(vw, index, node, e){
19906  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19907  });
19908
19909  // load XML data
19910  dataModel.load("foobar.xml");
19911  </code></pre>
19912  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19913  * <br><br>
19914  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19915  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19916  * 
19917  * Note: old style constructor is still suported (container, template, config)
19918  * 
19919  * @constructor
19920  * Create a new View
19921  * @param {Object} config The config object
19922  * 
19923  */
19924 Roo.View = function(config, depreciated_tpl, depreciated_config){
19925     
19926     this.parent = false;
19927     
19928     if (typeof(depreciated_tpl) == 'undefined') {
19929         // new way.. - universal constructor.
19930         Roo.apply(this, config);
19931         this.el  = Roo.get(this.el);
19932     } else {
19933         // old format..
19934         this.el  = Roo.get(config);
19935         this.tpl = depreciated_tpl;
19936         Roo.apply(this, depreciated_config);
19937     }
19938     this.wrapEl  = this.el.wrap().wrap();
19939     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19940     
19941     
19942     if(typeof(this.tpl) == "string"){
19943         this.tpl = new Roo.Template(this.tpl);
19944     } else {
19945         // support xtype ctors..
19946         this.tpl = new Roo.factory(this.tpl, Roo);
19947     }
19948     
19949     
19950     this.tpl.compile();
19951     
19952     /** @private */
19953     this.addEvents({
19954         /**
19955          * @event beforeclick
19956          * Fires before a click is processed. Returns false to cancel the default action.
19957          * @param {Roo.View} this
19958          * @param {Number} index The index of the target node
19959          * @param {HTMLElement} node The target node
19960          * @param {Roo.EventObject} e The raw event object
19961          */
19962             "beforeclick" : true,
19963         /**
19964          * @event click
19965          * Fires when a template node is clicked.
19966          * @param {Roo.View} this
19967          * @param {Number} index The index of the target node
19968          * @param {HTMLElement} node The target node
19969          * @param {Roo.EventObject} e The raw event object
19970          */
19971             "click" : true,
19972         /**
19973          * @event dblclick
19974          * Fires when a template node is double clicked.
19975          * @param {Roo.View} this
19976          * @param {Number} index The index of the target node
19977          * @param {HTMLElement} node The target node
19978          * @param {Roo.EventObject} e The raw event object
19979          */
19980             "dblclick" : true,
19981         /**
19982          * @event contextmenu
19983          * Fires when a template node is right clicked.
19984          * @param {Roo.View} this
19985          * @param {Number} index The index of the target node
19986          * @param {HTMLElement} node The target node
19987          * @param {Roo.EventObject} e The raw event object
19988          */
19989             "contextmenu" : true,
19990         /**
19991          * @event selectionchange
19992          * Fires when the selected nodes change.
19993          * @param {Roo.View} this
19994          * @param {Array} selections Array of the selected nodes
19995          */
19996             "selectionchange" : true,
19997     
19998         /**
19999          * @event beforeselect
20000          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20001          * @param {Roo.View} this
20002          * @param {HTMLElement} node The node to be selected
20003          * @param {Array} selections Array of currently selected nodes
20004          */
20005             "beforeselect" : true,
20006         /**
20007          * @event preparedata
20008          * Fires on every row to render, to allow you to change the data.
20009          * @param {Roo.View} this
20010          * @param {Object} data to be rendered (change this)
20011          */
20012           "preparedata" : true
20013           
20014           
20015         });
20016
20017
20018
20019     this.el.on({
20020         "click": this.onClick,
20021         "dblclick": this.onDblClick,
20022         "contextmenu": this.onContextMenu,
20023         scope:this
20024     });
20025
20026     this.selections = [];
20027     this.nodes = [];
20028     this.cmp = new Roo.CompositeElementLite([]);
20029     if(this.store){
20030         this.store = Roo.factory(this.store, Roo.data);
20031         this.setStore(this.store, true);
20032     }
20033     
20034     if ( this.footer && this.footer.xtype) {
20035            
20036          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20037         
20038         this.footer.dataSource = this.store;
20039         this.footer.container = fctr;
20040         this.footer = Roo.factory(this.footer, Roo);
20041         fctr.insertFirst(this.el);
20042         
20043         // this is a bit insane - as the paging toolbar seems to detach the el..
20044 //        dom.parentNode.parentNode.parentNode
20045          // they get detached?
20046     }
20047     
20048     
20049     Roo.View.superclass.constructor.call(this);
20050     
20051     
20052 };
20053
20054 Roo.extend(Roo.View, Roo.util.Observable, {
20055     
20056      /**
20057      * @cfg {Roo.data.Store} store Data store to load data from.
20058      */
20059     store : false,
20060     
20061     /**
20062      * @cfg {String|Roo.Element} el The container element.
20063      */
20064     el : '',
20065     
20066     /**
20067      * @cfg {String|Roo.Template} tpl The template used by this View 
20068      */
20069     tpl : false,
20070     /**
20071      * @cfg {String} dataName the named area of the template to use as the data area
20072      *                          Works with domtemplates roo-name="name"
20073      */
20074     dataName: false,
20075     /**
20076      * @cfg {String} selectedClass The css class to add to selected nodes
20077      */
20078     selectedClass : "x-view-selected",
20079      /**
20080      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20081      */
20082     emptyText : "",
20083     
20084     /**
20085      * @cfg {String} text to display on mask (default Loading)
20086      */
20087     mask : false,
20088     /**
20089      * @cfg {Boolean} multiSelect Allow multiple selection
20090      */
20091     multiSelect : false,
20092     /**
20093      * @cfg {Boolean} singleSelect Allow single selection
20094      */
20095     singleSelect:  false,
20096     
20097     /**
20098      * @cfg {Boolean} toggleSelect - selecting 
20099      */
20100     toggleSelect : false,
20101     
20102     /**
20103      * @cfg {Boolean} tickable - selecting 
20104      */
20105     tickable : false,
20106     
20107     /**
20108      * Returns the element this view is bound to.
20109      * @return {Roo.Element}
20110      */
20111     getEl : function(){
20112         return this.wrapEl;
20113     },
20114     
20115     
20116
20117     /**
20118      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20119      */
20120     refresh : function(){
20121         //Roo.log('refresh');
20122         var t = this.tpl;
20123         
20124         // if we are using something like 'domtemplate', then
20125         // the what gets used is:
20126         // t.applySubtemplate(NAME, data, wrapping data..)
20127         // the outer template then get' applied with
20128         //     the store 'extra data'
20129         // and the body get's added to the
20130         //      roo-name="data" node?
20131         //      <span class='roo-tpl-{name}'></span> ?????
20132         
20133         
20134         
20135         this.clearSelections();
20136         this.el.update("");
20137         var html = [];
20138         var records = this.store.getRange();
20139         if(records.length < 1) {
20140             
20141             // is this valid??  = should it render a template??
20142             
20143             this.el.update(this.emptyText);
20144             return;
20145         }
20146         var el = this.el;
20147         if (this.dataName) {
20148             this.el.update(t.apply(this.store.meta)); //????
20149             el = this.el.child('.roo-tpl-' + this.dataName);
20150         }
20151         
20152         for(var i = 0, len = records.length; i < len; i++){
20153             var data = this.prepareData(records[i].data, i, records[i]);
20154             this.fireEvent("preparedata", this, data, i, records[i]);
20155             
20156             var d = Roo.apply({}, data);
20157             
20158             if(this.tickable){
20159                 Roo.apply(d, {'roo-id' : Roo.id()});
20160                 
20161                 var _this = this;
20162             
20163                 Roo.each(this.parent.item, function(item){
20164                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20165                         return;
20166                     }
20167                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20168                 });
20169             }
20170             
20171             html[html.length] = Roo.util.Format.trim(
20172                 this.dataName ?
20173                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20174                     t.apply(d)
20175             );
20176         }
20177         
20178         
20179         
20180         el.update(html.join(""));
20181         this.nodes = el.dom.childNodes;
20182         this.updateIndexes(0);
20183     },
20184     
20185
20186     /**
20187      * Function to override to reformat the data that is sent to
20188      * the template for each node.
20189      * DEPRICATED - use the preparedata event handler.
20190      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20191      * a JSON object for an UpdateManager bound view).
20192      */
20193     prepareData : function(data, index, record)
20194     {
20195         this.fireEvent("preparedata", this, data, index, record);
20196         return data;
20197     },
20198
20199     onUpdate : function(ds, record){
20200         // Roo.log('on update');   
20201         this.clearSelections();
20202         var index = this.store.indexOf(record);
20203         var n = this.nodes[index];
20204         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20205         n.parentNode.removeChild(n);
20206         this.updateIndexes(index, index);
20207     },
20208
20209     
20210     
20211 // --------- FIXME     
20212     onAdd : function(ds, records, index)
20213     {
20214         //Roo.log(['on Add', ds, records, index] );        
20215         this.clearSelections();
20216         if(this.nodes.length == 0){
20217             this.refresh();
20218             return;
20219         }
20220         var n = this.nodes[index];
20221         for(var i = 0, len = records.length; i < len; i++){
20222             var d = this.prepareData(records[i].data, i, records[i]);
20223             if(n){
20224                 this.tpl.insertBefore(n, d);
20225             }else{
20226                 
20227                 this.tpl.append(this.el, d);
20228             }
20229         }
20230         this.updateIndexes(index);
20231     },
20232
20233     onRemove : function(ds, record, index){
20234        // Roo.log('onRemove');
20235         this.clearSelections();
20236         var el = this.dataName  ?
20237             this.el.child('.roo-tpl-' + this.dataName) :
20238             this.el; 
20239         
20240         el.dom.removeChild(this.nodes[index]);
20241         this.updateIndexes(index);
20242     },
20243
20244     /**
20245      * Refresh an individual node.
20246      * @param {Number} index
20247      */
20248     refreshNode : function(index){
20249         this.onUpdate(this.store, this.store.getAt(index));
20250     },
20251
20252     updateIndexes : function(startIndex, endIndex){
20253         var ns = this.nodes;
20254         startIndex = startIndex || 0;
20255         endIndex = endIndex || ns.length - 1;
20256         for(var i = startIndex; i <= endIndex; i++){
20257             ns[i].nodeIndex = i;
20258         }
20259     },
20260
20261     /**
20262      * Changes the data store this view uses and refresh the view.
20263      * @param {Store} store
20264      */
20265     setStore : function(store, initial){
20266         if(!initial && this.store){
20267             this.store.un("datachanged", this.refresh);
20268             this.store.un("add", this.onAdd);
20269             this.store.un("remove", this.onRemove);
20270             this.store.un("update", this.onUpdate);
20271             this.store.un("clear", this.refresh);
20272             this.store.un("beforeload", this.onBeforeLoad);
20273             this.store.un("load", this.onLoad);
20274             this.store.un("loadexception", this.onLoad);
20275         }
20276         if(store){
20277           
20278             store.on("datachanged", this.refresh, this);
20279             store.on("add", this.onAdd, this);
20280             store.on("remove", this.onRemove, this);
20281             store.on("update", this.onUpdate, this);
20282             store.on("clear", this.refresh, this);
20283             store.on("beforeload", this.onBeforeLoad, this);
20284             store.on("load", this.onLoad, this);
20285             store.on("loadexception", this.onLoad, this);
20286         }
20287         
20288         if(store){
20289             this.refresh();
20290         }
20291     },
20292     /**
20293      * onbeforeLoad - masks the loading area.
20294      *
20295      */
20296     onBeforeLoad : function(store,opts)
20297     {
20298          //Roo.log('onBeforeLoad');   
20299         if (!opts.add) {
20300             this.el.update("");
20301         }
20302         this.el.mask(this.mask ? this.mask : "Loading" ); 
20303     },
20304     onLoad : function ()
20305     {
20306         this.el.unmask();
20307     },
20308     
20309
20310     /**
20311      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20312      * @param {HTMLElement} node
20313      * @return {HTMLElement} The template node
20314      */
20315     findItemFromChild : function(node){
20316         var el = this.dataName  ?
20317             this.el.child('.roo-tpl-' + this.dataName,true) :
20318             this.el.dom; 
20319         
20320         if(!node || node.parentNode == el){
20321                     return node;
20322             }
20323             var p = node.parentNode;
20324             while(p && p != el){
20325             if(p.parentNode == el){
20326                 return p;
20327             }
20328             p = p.parentNode;
20329         }
20330             return null;
20331     },
20332
20333     /** @ignore */
20334     onClick : function(e){
20335         var item = this.findItemFromChild(e.getTarget());
20336         if(item){
20337             var index = this.indexOf(item);
20338             if(this.onItemClick(item, index, e) !== false){
20339                 this.fireEvent("click", this, index, item, e);
20340             }
20341         }else{
20342             this.clearSelections();
20343         }
20344     },
20345
20346     /** @ignore */
20347     onContextMenu : function(e){
20348         var item = this.findItemFromChild(e.getTarget());
20349         if(item){
20350             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20351         }
20352     },
20353
20354     /** @ignore */
20355     onDblClick : function(e){
20356         var item = this.findItemFromChild(e.getTarget());
20357         if(item){
20358             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20359         }
20360     },
20361
20362     onItemClick : function(item, index, e)
20363     {
20364         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20365             return false;
20366         }
20367         if (this.toggleSelect) {
20368             var m = this.isSelected(item) ? 'unselect' : 'select';
20369             //Roo.log(m);
20370             var _t = this;
20371             _t[m](item, true, false);
20372             return true;
20373         }
20374         if(this.multiSelect || this.singleSelect){
20375             if(this.multiSelect && e.shiftKey && this.lastSelection){
20376                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20377             }else{
20378                 this.select(item, this.multiSelect && e.ctrlKey);
20379                 this.lastSelection = item;
20380             }
20381             
20382             if(!this.tickable){
20383                 e.preventDefault();
20384             }
20385             
20386         }
20387         return true;
20388     },
20389
20390     /**
20391      * Get the number of selected nodes.
20392      * @return {Number}
20393      */
20394     getSelectionCount : function(){
20395         return this.selections.length;
20396     },
20397
20398     /**
20399      * Get the currently selected nodes.
20400      * @return {Array} An array of HTMLElements
20401      */
20402     getSelectedNodes : function(){
20403         return this.selections;
20404     },
20405
20406     /**
20407      * Get the indexes of the selected nodes.
20408      * @return {Array}
20409      */
20410     getSelectedIndexes : function(){
20411         var indexes = [], s = this.selections;
20412         for(var i = 0, len = s.length; i < len; i++){
20413             indexes.push(s[i].nodeIndex);
20414         }
20415         return indexes;
20416     },
20417
20418     /**
20419      * Clear all selections
20420      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20421      */
20422     clearSelections : function(suppressEvent){
20423         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20424             this.cmp.elements = this.selections;
20425             this.cmp.removeClass(this.selectedClass);
20426             this.selections = [];
20427             if(!suppressEvent){
20428                 this.fireEvent("selectionchange", this, this.selections);
20429             }
20430         }
20431     },
20432
20433     /**
20434      * Returns true if the passed node is selected
20435      * @param {HTMLElement/Number} node The node or node index
20436      * @return {Boolean}
20437      */
20438     isSelected : function(node){
20439         var s = this.selections;
20440         if(s.length < 1){
20441             return false;
20442         }
20443         node = this.getNode(node);
20444         return s.indexOf(node) !== -1;
20445     },
20446
20447     /**
20448      * Selects nodes.
20449      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20450      * @param {Boolean} keepExisting (optional) true to keep existing selections
20451      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20452      */
20453     select : function(nodeInfo, keepExisting, suppressEvent){
20454         if(nodeInfo instanceof Array){
20455             if(!keepExisting){
20456                 this.clearSelections(true);
20457             }
20458             for(var i = 0, len = nodeInfo.length; i < len; i++){
20459                 this.select(nodeInfo[i], true, true);
20460             }
20461             return;
20462         } 
20463         var node = this.getNode(nodeInfo);
20464         if(!node || this.isSelected(node)){
20465             return; // already selected.
20466         }
20467         if(!keepExisting){
20468             this.clearSelections(true);
20469         }
20470         
20471         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20472             Roo.fly(node).addClass(this.selectedClass);
20473             this.selections.push(node);
20474             if(!suppressEvent){
20475                 this.fireEvent("selectionchange", this, this.selections);
20476             }
20477         }
20478         
20479         
20480     },
20481       /**
20482      * Unselects nodes.
20483      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20484      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20485      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20486      */
20487     unselect : function(nodeInfo, keepExisting, suppressEvent)
20488     {
20489         if(nodeInfo instanceof Array){
20490             Roo.each(this.selections, function(s) {
20491                 this.unselect(s, nodeInfo);
20492             }, this);
20493             return;
20494         }
20495         var node = this.getNode(nodeInfo);
20496         if(!node || !this.isSelected(node)){
20497             //Roo.log("not selected");
20498             return; // not selected.
20499         }
20500         // fireevent???
20501         var ns = [];
20502         Roo.each(this.selections, function(s) {
20503             if (s == node ) {
20504                 Roo.fly(node).removeClass(this.selectedClass);
20505
20506                 return;
20507             }
20508             ns.push(s);
20509         },this);
20510         
20511         this.selections= ns;
20512         this.fireEvent("selectionchange", this, this.selections);
20513     },
20514
20515     /**
20516      * Gets a template node.
20517      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20518      * @return {HTMLElement} The node or null if it wasn't found
20519      */
20520     getNode : function(nodeInfo){
20521         if(typeof nodeInfo == "string"){
20522             return document.getElementById(nodeInfo);
20523         }else if(typeof nodeInfo == "number"){
20524             return this.nodes[nodeInfo];
20525         }
20526         return nodeInfo;
20527     },
20528
20529     /**
20530      * Gets a range template nodes.
20531      * @param {Number} startIndex
20532      * @param {Number} endIndex
20533      * @return {Array} An array of nodes
20534      */
20535     getNodes : function(start, end){
20536         var ns = this.nodes;
20537         start = start || 0;
20538         end = typeof end == "undefined" ? ns.length - 1 : end;
20539         var nodes = [];
20540         if(start <= end){
20541             for(var i = start; i <= end; i++){
20542                 nodes.push(ns[i]);
20543             }
20544         } else{
20545             for(var i = start; i >= end; i--){
20546                 nodes.push(ns[i]);
20547             }
20548         }
20549         return nodes;
20550     },
20551
20552     /**
20553      * Finds the index of the passed node
20554      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20555      * @return {Number} The index of the node or -1
20556      */
20557     indexOf : function(node){
20558         node = this.getNode(node);
20559         if(typeof node.nodeIndex == "number"){
20560             return node.nodeIndex;
20561         }
20562         var ns = this.nodes;
20563         for(var i = 0, len = ns.length; i < len; i++){
20564             if(ns[i] == node){
20565                 return i;
20566             }
20567         }
20568         return -1;
20569     }
20570 });
20571 /*
20572  * - LGPL
20573  *
20574  * based on jquery fullcalendar
20575  * 
20576  */
20577
20578 Roo.bootstrap = Roo.bootstrap || {};
20579 /**
20580  * @class Roo.bootstrap.Calendar
20581  * @extends Roo.bootstrap.Component
20582  * Bootstrap Calendar class
20583  * @cfg {Boolean} loadMask (true|false) default false
20584  * @cfg {Object} header generate the user specific header of the calendar, default false
20585
20586  * @constructor
20587  * Create a new Container
20588  * @param {Object} config The config object
20589  */
20590
20591
20592
20593 Roo.bootstrap.Calendar = function(config){
20594     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20595      this.addEvents({
20596         /**
20597              * @event select
20598              * Fires when a date is selected
20599              * @param {DatePicker} this
20600              * @param {Date} date The selected date
20601              */
20602         'select': true,
20603         /**
20604              * @event monthchange
20605              * Fires when the displayed month changes 
20606              * @param {DatePicker} this
20607              * @param {Date} date The selected month
20608              */
20609         'monthchange': true,
20610         /**
20611              * @event evententer
20612              * Fires when mouse over an event
20613              * @param {Calendar} this
20614              * @param {event} Event
20615              */
20616         'evententer': true,
20617         /**
20618              * @event eventleave
20619              * Fires when the mouse leaves an
20620              * @param {Calendar} this
20621              * @param {event}
20622              */
20623         'eventleave': true,
20624         /**
20625              * @event eventclick
20626              * Fires when the mouse click an
20627              * @param {Calendar} this
20628              * @param {event}
20629              */
20630         'eventclick': true
20631         
20632     });
20633
20634 };
20635
20636 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20637     
20638           /**
20639      * @cfg {Roo.data.Store} store
20640      * The data source for the calendar
20641      */
20642         store : false,
20643      /**
20644      * @cfg {Number} startDay
20645      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20646      */
20647     startDay : 0,
20648     
20649     loadMask : false,
20650     
20651     header : false,
20652       
20653     getAutoCreate : function(){
20654         
20655         
20656         var fc_button = function(name, corner, style, content ) {
20657             return Roo.apply({},{
20658                 tag : 'span',
20659                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20660                          (corner.length ?
20661                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20662                             ''
20663                         ),
20664                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20665                 unselectable: 'on'
20666             });
20667         };
20668         
20669         var header = {};
20670         
20671         if(!this.header){
20672             header = {
20673                 tag : 'table',
20674                 cls : 'fc-header',
20675                 style : 'width:100%',
20676                 cn : [
20677                     {
20678                         tag: 'tr',
20679                         cn : [
20680                             {
20681                                 tag : 'td',
20682                                 cls : 'fc-header-left',
20683                                 cn : [
20684                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20685                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20686                                     { tag: 'span', cls: 'fc-header-space' },
20687                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20688
20689
20690                                 ]
20691                             },
20692
20693                             {
20694                                 tag : 'td',
20695                                 cls : 'fc-header-center',
20696                                 cn : [
20697                                     {
20698                                         tag: 'span',
20699                                         cls: 'fc-header-title',
20700                                         cn : {
20701                                             tag: 'H2',
20702                                             html : 'month / year'
20703                                         }
20704                                     }
20705
20706                                 ]
20707                             },
20708                             {
20709                                 tag : 'td',
20710                                 cls : 'fc-header-right',
20711                                 cn : [
20712                               /*      fc_button('month', 'left', '', 'month' ),
20713                                     fc_button('week', '', '', 'week' ),
20714                                     fc_button('day', 'right', '', 'day' )
20715                                 */    
20716
20717                                 ]
20718                             }
20719
20720                         ]
20721                     }
20722                 ]
20723             };
20724         }
20725         
20726         header = this.header;
20727         
20728        
20729         var cal_heads = function() {
20730             var ret = [];
20731             // fixme - handle this.
20732             
20733             for (var i =0; i < Date.dayNames.length; i++) {
20734                 var d = Date.dayNames[i];
20735                 ret.push({
20736                     tag: 'th',
20737                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20738                     html : d.substring(0,3)
20739                 });
20740                 
20741             }
20742             ret[0].cls += ' fc-first';
20743             ret[6].cls += ' fc-last';
20744             return ret;
20745         };
20746         var cal_cell = function(n) {
20747             return  {
20748                 tag: 'td',
20749                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20750                 cn : [
20751                     {
20752                         cn : [
20753                             {
20754                                 cls: 'fc-day-number',
20755                                 html: 'D'
20756                             },
20757                             {
20758                                 cls: 'fc-day-content',
20759                              
20760                                 cn : [
20761                                      {
20762                                         style: 'position: relative;' // height: 17px;
20763                                     }
20764                                 ]
20765                             }
20766                             
20767                             
20768                         ]
20769                     }
20770                 ]
20771                 
20772             }
20773         };
20774         var cal_rows = function() {
20775             
20776             var ret = [];
20777             for (var r = 0; r < 6; r++) {
20778                 var row= {
20779                     tag : 'tr',
20780                     cls : 'fc-week',
20781                     cn : []
20782                 };
20783                 
20784                 for (var i =0; i < Date.dayNames.length; i++) {
20785                     var d = Date.dayNames[i];
20786                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20787
20788                 }
20789                 row.cn[0].cls+=' fc-first';
20790                 row.cn[0].cn[0].style = 'min-height:90px';
20791                 row.cn[6].cls+=' fc-last';
20792                 ret.push(row);
20793                 
20794             }
20795             ret[0].cls += ' fc-first';
20796             ret[4].cls += ' fc-prev-last';
20797             ret[5].cls += ' fc-last';
20798             return ret;
20799             
20800         };
20801         
20802         var cal_table = {
20803             tag: 'table',
20804             cls: 'fc-border-separate',
20805             style : 'width:100%',
20806             cellspacing  : 0,
20807             cn : [
20808                 { 
20809                     tag: 'thead',
20810                     cn : [
20811                         { 
20812                             tag: 'tr',
20813                             cls : 'fc-first fc-last',
20814                             cn : cal_heads()
20815                         }
20816                     ]
20817                 },
20818                 { 
20819                     tag: 'tbody',
20820                     cn : cal_rows()
20821                 }
20822                   
20823             ]
20824         };
20825          
20826          var cfg = {
20827             cls : 'fc fc-ltr',
20828             cn : [
20829                 header,
20830                 {
20831                     cls : 'fc-content',
20832                     style : "position: relative;",
20833                     cn : [
20834                         {
20835                             cls : 'fc-view fc-view-month fc-grid',
20836                             style : 'position: relative',
20837                             unselectable : 'on',
20838                             cn : [
20839                                 {
20840                                     cls : 'fc-event-container',
20841                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20842                                 },
20843                                 cal_table
20844                             ]
20845                         }
20846                     ]
20847     
20848                 }
20849            ] 
20850             
20851         };
20852         
20853          
20854         
20855         return cfg;
20856     },
20857     
20858     
20859     initEvents : function()
20860     {
20861         if(!this.store){
20862             throw "can not find store for calendar";
20863         }
20864         
20865         var mark = {
20866             tag: "div",
20867             cls:"x-dlg-mask",
20868             style: "text-align:center",
20869             cn: [
20870                 {
20871                     tag: "div",
20872                     style: "background-color:white;width:50%;margin:250 auto",
20873                     cn: [
20874                         {
20875                             tag: "img",
20876                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20877                         },
20878                         {
20879                             tag: "span",
20880                             html: "Loading"
20881                         }
20882                         
20883                     ]
20884                 }
20885             ]
20886         };
20887         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20888         
20889         var size = this.el.select('.fc-content', true).first().getSize();
20890         this.maskEl.setSize(size.width, size.height);
20891         this.maskEl.enableDisplayMode("block");
20892         if(!this.loadMask){
20893             this.maskEl.hide();
20894         }
20895         
20896         this.store = Roo.factory(this.store, Roo.data);
20897         this.store.on('load', this.onLoad, this);
20898         this.store.on('beforeload', this.onBeforeLoad, this);
20899         
20900         this.resize();
20901         
20902         this.cells = this.el.select('.fc-day',true);
20903         //Roo.log(this.cells);
20904         this.textNodes = this.el.query('.fc-day-number');
20905         this.cells.addClassOnOver('fc-state-hover');
20906         
20907         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20908         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20909         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20910         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20911         
20912         this.on('monthchange', this.onMonthChange, this);
20913         
20914         this.update(new Date().clearTime());
20915     },
20916     
20917     resize : function() {
20918         var sz  = this.el.getSize();
20919         
20920         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20921         this.el.select('.fc-day-content div',true).setHeight(34);
20922     },
20923     
20924     
20925     // private
20926     showPrevMonth : function(e){
20927         this.update(this.activeDate.add("mo", -1));
20928     },
20929     showToday : function(e){
20930         this.update(new Date().clearTime());
20931     },
20932     // private
20933     showNextMonth : function(e){
20934         this.update(this.activeDate.add("mo", 1));
20935     },
20936
20937     // private
20938     showPrevYear : function(){
20939         this.update(this.activeDate.add("y", -1));
20940     },
20941
20942     // private
20943     showNextYear : function(){
20944         this.update(this.activeDate.add("y", 1));
20945     },
20946
20947     
20948    // private
20949     update : function(date)
20950     {
20951         var vd = this.activeDate;
20952         this.activeDate = date;
20953 //        if(vd && this.el){
20954 //            var t = date.getTime();
20955 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20956 //                Roo.log('using add remove');
20957 //                
20958 //                this.fireEvent('monthchange', this, date);
20959 //                
20960 //                this.cells.removeClass("fc-state-highlight");
20961 //                this.cells.each(function(c){
20962 //                   if(c.dateValue == t){
20963 //                       c.addClass("fc-state-highlight");
20964 //                       setTimeout(function(){
20965 //                            try{c.dom.firstChild.focus();}catch(e){}
20966 //                       }, 50);
20967 //                       return false;
20968 //                   }
20969 //                   return true;
20970 //                });
20971 //                return;
20972 //            }
20973 //        }
20974         
20975         var days = date.getDaysInMonth();
20976         
20977         var firstOfMonth = date.getFirstDateOfMonth();
20978         var startingPos = firstOfMonth.getDay()-this.startDay;
20979         
20980         if(startingPos < this.startDay){
20981             startingPos += 7;
20982         }
20983         
20984         var pm = date.add(Date.MONTH, -1);
20985         var prevStart = pm.getDaysInMonth()-startingPos;
20986 //        
20987         this.cells = this.el.select('.fc-day',true);
20988         this.textNodes = this.el.query('.fc-day-number');
20989         this.cells.addClassOnOver('fc-state-hover');
20990         
20991         var cells = this.cells.elements;
20992         var textEls = this.textNodes;
20993         
20994         Roo.each(cells, function(cell){
20995             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20996         });
20997         
20998         days += startingPos;
20999
21000         // convert everything to numbers so it's fast
21001         var day = 86400000;
21002         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21003         //Roo.log(d);
21004         //Roo.log(pm);
21005         //Roo.log(prevStart);
21006         
21007         var today = new Date().clearTime().getTime();
21008         var sel = date.clearTime().getTime();
21009         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21010         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21011         var ddMatch = this.disabledDatesRE;
21012         var ddText = this.disabledDatesText;
21013         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21014         var ddaysText = this.disabledDaysText;
21015         var format = this.format;
21016         
21017         var setCellClass = function(cal, cell){
21018             cell.row = 0;
21019             cell.events = [];
21020             cell.more = [];
21021             //Roo.log('set Cell Class');
21022             cell.title = "";
21023             var t = d.getTime();
21024             
21025             //Roo.log(d);
21026             
21027             cell.dateValue = t;
21028             if(t == today){
21029                 cell.className += " fc-today";
21030                 cell.className += " fc-state-highlight";
21031                 cell.title = cal.todayText;
21032             }
21033             if(t == sel){
21034                 // disable highlight in other month..
21035                 //cell.className += " fc-state-highlight";
21036                 
21037             }
21038             // disabling
21039             if(t < min) {
21040                 cell.className = " fc-state-disabled";
21041                 cell.title = cal.minText;
21042                 return;
21043             }
21044             if(t > max) {
21045                 cell.className = " fc-state-disabled";
21046                 cell.title = cal.maxText;
21047                 return;
21048             }
21049             if(ddays){
21050                 if(ddays.indexOf(d.getDay()) != -1){
21051                     cell.title = ddaysText;
21052                     cell.className = " fc-state-disabled";
21053                 }
21054             }
21055             if(ddMatch && format){
21056                 var fvalue = d.dateFormat(format);
21057                 if(ddMatch.test(fvalue)){
21058                     cell.title = ddText.replace("%0", fvalue);
21059                     cell.className = " fc-state-disabled";
21060                 }
21061             }
21062             
21063             if (!cell.initialClassName) {
21064                 cell.initialClassName = cell.dom.className;
21065             }
21066             
21067             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21068         };
21069
21070         var i = 0;
21071         
21072         for(; i < startingPos; i++) {
21073             textEls[i].innerHTML = (++prevStart);
21074             d.setDate(d.getDate()+1);
21075             
21076             cells[i].className = "fc-past fc-other-month";
21077             setCellClass(this, cells[i]);
21078         }
21079         
21080         var intDay = 0;
21081         
21082         for(; i < days; i++){
21083             intDay = i - startingPos + 1;
21084             textEls[i].innerHTML = (intDay);
21085             d.setDate(d.getDate()+1);
21086             
21087             cells[i].className = ''; // "x-date-active";
21088             setCellClass(this, cells[i]);
21089         }
21090         var extraDays = 0;
21091         
21092         for(; i < 42; i++) {
21093             textEls[i].innerHTML = (++extraDays);
21094             d.setDate(d.getDate()+1);
21095             
21096             cells[i].className = "fc-future fc-other-month";
21097             setCellClass(this, cells[i]);
21098         }
21099         
21100         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21101         
21102         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21103         
21104         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21105         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21106         
21107         if(totalRows != 6){
21108             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21109             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21110         }
21111         
21112         this.fireEvent('monthchange', this, date);
21113         
21114         
21115         /*
21116         if(!this.internalRender){
21117             var main = this.el.dom.firstChild;
21118             var w = main.offsetWidth;
21119             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21120             Roo.fly(main).setWidth(w);
21121             this.internalRender = true;
21122             // opera does not respect the auto grow header center column
21123             // then, after it gets a width opera refuses to recalculate
21124             // without a second pass
21125             if(Roo.isOpera && !this.secondPass){
21126                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21127                 this.secondPass = true;
21128                 this.update.defer(10, this, [date]);
21129             }
21130         }
21131         */
21132         
21133     },
21134     
21135     findCell : function(dt) {
21136         dt = dt.clearTime().getTime();
21137         var ret = false;
21138         this.cells.each(function(c){
21139             //Roo.log("check " +c.dateValue + '?=' + dt);
21140             if(c.dateValue == dt){
21141                 ret = c;
21142                 return false;
21143             }
21144             return true;
21145         });
21146         
21147         return ret;
21148     },
21149     
21150     findCells : function(ev) {
21151         var s = ev.start.clone().clearTime().getTime();
21152        // Roo.log(s);
21153         var e= ev.end.clone().clearTime().getTime();
21154        // Roo.log(e);
21155         var ret = [];
21156         this.cells.each(function(c){
21157              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21158             
21159             if(c.dateValue > e){
21160                 return ;
21161             }
21162             if(c.dateValue < s){
21163                 return ;
21164             }
21165             ret.push(c);
21166         });
21167         
21168         return ret;    
21169     },
21170     
21171 //    findBestRow: function(cells)
21172 //    {
21173 //        var ret = 0;
21174 //        
21175 //        for (var i =0 ; i < cells.length;i++) {
21176 //            ret  = Math.max(cells[i].rows || 0,ret);
21177 //        }
21178 //        return ret;
21179 //        
21180 //    },
21181     
21182     
21183     addItem : function(ev)
21184     {
21185         // look for vertical location slot in
21186         var cells = this.findCells(ev);
21187         
21188 //        ev.row = this.findBestRow(cells);
21189         
21190         // work out the location.
21191         
21192         var crow = false;
21193         var rows = [];
21194         for(var i =0; i < cells.length; i++) {
21195             
21196             cells[i].row = cells[0].row;
21197             
21198             if(i == 0){
21199                 cells[i].row = cells[i].row + 1;
21200             }
21201             
21202             if (!crow) {
21203                 crow = {
21204                     start : cells[i],
21205                     end :  cells[i]
21206                 };
21207                 continue;
21208             }
21209             if (crow.start.getY() == cells[i].getY()) {
21210                 // on same row.
21211                 crow.end = cells[i];
21212                 continue;
21213             }
21214             // different row.
21215             rows.push(crow);
21216             crow = {
21217                 start: cells[i],
21218                 end : cells[i]
21219             };
21220             
21221         }
21222         
21223         rows.push(crow);
21224         ev.els = [];
21225         ev.rows = rows;
21226         ev.cells = cells;
21227         
21228         cells[0].events.push(ev);
21229         
21230         this.calevents.push(ev);
21231     },
21232     
21233     clearEvents: function() {
21234         
21235         if(!this.calevents){
21236             return;
21237         }
21238         
21239         Roo.each(this.cells.elements, function(c){
21240             c.row = 0;
21241             c.events = [];
21242             c.more = [];
21243         });
21244         
21245         Roo.each(this.calevents, function(e) {
21246             Roo.each(e.els, function(el) {
21247                 el.un('mouseenter' ,this.onEventEnter, this);
21248                 el.un('mouseleave' ,this.onEventLeave, this);
21249                 el.remove();
21250             },this);
21251         },this);
21252         
21253         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21254             e.remove();
21255         });
21256         
21257     },
21258     
21259     renderEvents: function()
21260     {   
21261         var _this = this;
21262         
21263         this.cells.each(function(c) {
21264             
21265             if(c.row < 5){
21266                 return;
21267             }
21268             
21269             var ev = c.events;
21270             
21271             var r = 4;
21272             if(c.row != c.events.length){
21273                 r = 4 - (4 - (c.row - c.events.length));
21274             }
21275             
21276             c.events = ev.slice(0, r);
21277             c.more = ev.slice(r);
21278             
21279             if(c.more.length && c.more.length == 1){
21280                 c.events.push(c.more.pop());
21281             }
21282             
21283             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21284             
21285         });
21286             
21287         this.cells.each(function(c) {
21288             
21289             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21290             
21291             
21292             for (var e = 0; e < c.events.length; e++){
21293                 var ev = c.events[e];
21294                 var rows = ev.rows;
21295                 
21296                 for(var i = 0; i < rows.length; i++) {
21297                 
21298                     // how many rows should it span..
21299
21300                     var  cfg = {
21301                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21302                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21303
21304                         unselectable : "on",
21305                         cn : [
21306                             {
21307                                 cls: 'fc-event-inner',
21308                                 cn : [
21309     //                                {
21310     //                                  tag:'span',
21311     //                                  cls: 'fc-event-time',
21312     //                                  html : cells.length > 1 ? '' : ev.time
21313     //                                },
21314                                     {
21315                                       tag:'span',
21316                                       cls: 'fc-event-title',
21317                                       html : String.format('{0}', ev.title)
21318                                     }
21319
21320
21321                                 ]
21322                             },
21323                             {
21324                                 cls: 'ui-resizable-handle ui-resizable-e',
21325                                 html : '&nbsp;&nbsp;&nbsp'
21326                             }
21327
21328                         ]
21329                     };
21330
21331                     if (i == 0) {
21332                         cfg.cls += ' fc-event-start';
21333                     }
21334                     if ((i+1) == rows.length) {
21335                         cfg.cls += ' fc-event-end';
21336                     }
21337
21338                     var ctr = _this.el.select('.fc-event-container',true).first();
21339                     var cg = ctr.createChild(cfg);
21340
21341                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21342                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21343
21344                     var r = (c.more.length) ? 1 : 0;
21345                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21346                     cg.setWidth(ebox.right - sbox.x -2);
21347
21348                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21349                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21350                     cg.on('click', _this.onEventClick, _this, ev);
21351
21352                     ev.els.push(cg);
21353                     
21354                 }
21355                 
21356             }
21357             
21358             
21359             if(c.more.length){
21360                 var  cfg = {
21361                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21362                     style : 'position: absolute',
21363                     unselectable : "on",
21364                     cn : [
21365                         {
21366                             cls: 'fc-event-inner',
21367                             cn : [
21368                                 {
21369                                   tag:'span',
21370                                   cls: 'fc-event-title',
21371                                   html : 'More'
21372                                 }
21373
21374
21375                             ]
21376                         },
21377                         {
21378                             cls: 'ui-resizable-handle ui-resizable-e',
21379                             html : '&nbsp;&nbsp;&nbsp'
21380                         }
21381
21382                     ]
21383                 };
21384
21385                 var ctr = _this.el.select('.fc-event-container',true).first();
21386                 var cg = ctr.createChild(cfg);
21387
21388                 var sbox = c.select('.fc-day-content',true).first().getBox();
21389                 var ebox = c.select('.fc-day-content',true).first().getBox();
21390                 //Roo.log(cg);
21391                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21392                 cg.setWidth(ebox.right - sbox.x -2);
21393
21394                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21395                 
21396             }
21397             
21398         });
21399         
21400         
21401         
21402     },
21403     
21404     onEventEnter: function (e, el,event,d) {
21405         this.fireEvent('evententer', this, el, event);
21406     },
21407     
21408     onEventLeave: function (e, el,event,d) {
21409         this.fireEvent('eventleave', this, el, event);
21410     },
21411     
21412     onEventClick: function (e, el,event,d) {
21413         this.fireEvent('eventclick', this, el, event);
21414     },
21415     
21416     onMonthChange: function () {
21417         this.store.load();
21418     },
21419     
21420     onMoreEventClick: function(e, el, more)
21421     {
21422         var _this = this;
21423         
21424         this.calpopover.placement = 'right';
21425         this.calpopover.setTitle('More');
21426         
21427         this.calpopover.setContent('');
21428         
21429         var ctr = this.calpopover.el.select('.popover-content', true).first();
21430         
21431         Roo.each(more, function(m){
21432             var cfg = {
21433                 cls : 'fc-event-hori fc-event-draggable',
21434                 html : m.title
21435             };
21436             var cg = ctr.createChild(cfg);
21437             
21438             cg.on('click', _this.onEventClick, _this, m);
21439         });
21440         
21441         this.calpopover.show(el);
21442         
21443         
21444     },
21445     
21446     onLoad: function () 
21447     {   
21448         this.calevents = [];
21449         var cal = this;
21450         
21451         if(this.store.getCount() > 0){
21452             this.store.data.each(function(d){
21453                cal.addItem({
21454                     id : d.data.id,
21455                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21456                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21457                     time : d.data.start_time,
21458                     title : d.data.title,
21459                     description : d.data.description,
21460                     venue : d.data.venue
21461                 });
21462             });
21463         }
21464         
21465         this.renderEvents();
21466         
21467         if(this.calevents.length && this.loadMask){
21468             this.maskEl.hide();
21469         }
21470     },
21471     
21472     onBeforeLoad: function()
21473     {
21474         this.clearEvents();
21475         if(this.loadMask){
21476             this.maskEl.show();
21477         }
21478     }
21479 });
21480
21481  
21482  /*
21483  * - LGPL
21484  *
21485  * element
21486  * 
21487  */
21488
21489 /**
21490  * @class Roo.bootstrap.Popover
21491  * @extends Roo.bootstrap.Component
21492  * @builder-top
21493  * @parent none
21494  * @children Roo.bootstrap.Component
21495  * Bootstrap Popover class
21496  * @cfg {String} html contents of the popover   (or false to use children..)
21497  * @cfg {String} title of popover (or false to hide)
21498  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21499  * @cfg {String} trigger click || hover (or false to trigger manually)
21500  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21501  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21502  *      - if false and it has a 'parent' then it will be automatically added to that element
21503  *      - if string - Roo.get  will be called 
21504  * @cfg {Number} delay - delay before showing
21505  
21506  * @constructor
21507  * Create a new Popover
21508  * @param {Object} config The config object
21509  */
21510
21511 Roo.bootstrap.Popover = function(config){
21512     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21513     
21514     this.addEvents({
21515         // raw events
21516          /**
21517          * @event show
21518          * After the popover show
21519          * 
21520          * @param {Roo.bootstrap.Popover} this
21521          */
21522         "show" : true,
21523         /**
21524          * @event hide
21525          * After the popover hide
21526          * 
21527          * @param {Roo.bootstrap.Popover} this
21528          */
21529         "hide" : true
21530     });
21531 };
21532
21533 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21534     
21535     title: false,
21536     html: false,
21537     
21538     placement : 'right',
21539     trigger : 'hover', // hover
21540     modal : false,
21541     delay : 0,
21542     
21543     over: false,
21544     
21545     can_build_overlaid : false,
21546     
21547     maskEl : false, // the mask element
21548     headerEl : false,
21549     contentEl : false,
21550     alignEl : false, // when show is called with an element - this get's stored.
21551     
21552     getChildContainer : function()
21553     {
21554         return this.contentEl;
21555         
21556     },
21557     getPopoverHeader : function()
21558     {
21559         this.title = true; // flag not to hide it..
21560         this.headerEl.addClass('p-0');
21561         return this.headerEl
21562     },
21563     
21564     
21565     getAutoCreate : function(){
21566          
21567         var cfg = {
21568            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21569            style: 'display:block',
21570            cn : [
21571                 {
21572                     cls : 'arrow'
21573                 },
21574                 {
21575                     cls : 'popover-inner ',
21576                     cn : [
21577                         {
21578                             tag: 'h3',
21579                             cls: 'popover-title popover-header',
21580                             html : this.title === false ? '' : this.title
21581                         },
21582                         {
21583                             cls : 'popover-content popover-body '  + (this.cls || ''),
21584                             html : this.html || ''
21585                         }
21586                     ]
21587                     
21588                 }
21589            ]
21590         };
21591         
21592         return cfg;
21593     },
21594     /**
21595      * @param {string} the title
21596      */
21597     setTitle: function(str)
21598     {
21599         this.title = str;
21600         if (this.el) {
21601             this.headerEl.dom.innerHTML = str;
21602         }
21603         
21604     },
21605     /**
21606      * @param {string} the body content
21607      */
21608     setContent: function(str)
21609     {
21610         this.html = str;
21611         if (this.contentEl) {
21612             this.contentEl.dom.innerHTML = str;
21613         }
21614         
21615     },
21616     // as it get's added to the bottom of the page.
21617     onRender : function(ct, position)
21618     {
21619         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21620         
21621         
21622         
21623         if(!this.el){
21624             var cfg = Roo.apply({},  this.getAutoCreate());
21625             cfg.id = Roo.id();
21626             
21627             if (this.cls) {
21628                 cfg.cls += ' ' + this.cls;
21629             }
21630             if (this.style) {
21631                 cfg.style = this.style;
21632             }
21633             //Roo.log("adding to ");
21634             this.el = Roo.get(document.body).createChild(cfg, position);
21635 //            Roo.log(this.el);
21636         }
21637         
21638         this.contentEl = this.el.select('.popover-content',true).first();
21639         this.headerEl =  this.el.select('.popover-title',true).first();
21640         
21641         var nitems = [];
21642         if(typeof(this.items) != 'undefined'){
21643             var items = this.items;
21644             delete this.items;
21645
21646             for(var i =0;i < items.length;i++) {
21647                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21648             }
21649         }
21650
21651         this.items = nitems;
21652         
21653         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21654         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21655         
21656         
21657         
21658         this.initEvents();
21659     },
21660     
21661     resizeMask : function()
21662     {
21663         this.maskEl.setSize(
21664             Roo.lib.Dom.getViewWidth(true),
21665             Roo.lib.Dom.getViewHeight(true)
21666         );
21667     },
21668     
21669     initEvents : function()
21670     {
21671         
21672         if (!this.modal) { 
21673             Roo.bootstrap.Popover.register(this);
21674         }
21675          
21676         this.arrowEl = this.el.select('.arrow',true).first();
21677         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21678         this.el.enableDisplayMode('block');
21679         this.el.hide();
21680  
21681         
21682         if (this.over === false && !this.parent()) {
21683             return; 
21684         }
21685         if (this.triggers === false) {
21686             return;
21687         }
21688          
21689         // support parent
21690         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21691         var triggers = this.trigger ? this.trigger.split(' ') : [];
21692         Roo.each(triggers, function(trigger) {
21693         
21694             if (trigger == 'click') {
21695                 on_el.on('click', this.toggle, this);
21696             } else if (trigger != 'manual') {
21697                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21698                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21699       
21700                 on_el.on(eventIn  ,this.enter, this);
21701                 on_el.on(eventOut, this.leave, this);
21702             }
21703         }, this);
21704     },
21705     
21706     
21707     // private
21708     timeout : null,
21709     hoverState : null,
21710     
21711     toggle : function () {
21712         this.hoverState == 'in' ? this.leave() : this.enter();
21713     },
21714     
21715     enter : function () {
21716         
21717         clearTimeout(this.timeout);
21718     
21719         this.hoverState = 'in';
21720     
21721         if (!this.delay || !this.delay.show) {
21722             this.show();
21723             return;
21724         }
21725         var _t = this;
21726         this.timeout = setTimeout(function () {
21727             if (_t.hoverState == 'in') {
21728                 _t.show();
21729             }
21730         }, this.delay.show)
21731     },
21732     
21733     leave : function() {
21734         clearTimeout(this.timeout);
21735     
21736         this.hoverState = 'out';
21737     
21738         if (!this.delay || !this.delay.hide) {
21739             this.hide();
21740             return;
21741         }
21742         var _t = this;
21743         this.timeout = setTimeout(function () {
21744             if (_t.hoverState == 'out') {
21745                 _t.hide();
21746             }
21747         }, this.delay.hide)
21748     },
21749     
21750     /**
21751      * update the position of the dialog
21752      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21753      * 
21754      *
21755      */
21756     
21757     doAlign : function()
21758     {
21759         
21760         if (this.alignEl) {
21761             this.updatePosition(this.placement, true);
21762              
21763         } else {
21764             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21765             var es = this.el.getSize();
21766             var x = Roo.lib.Dom.getViewWidth()/2;
21767             var y = Roo.lib.Dom.getViewHeight()/2;
21768             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21769             
21770         }
21771
21772          
21773          
21774         
21775         
21776     },
21777     
21778     /**
21779      * Show the popover
21780      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21781      * @param {string} (left|right|top|bottom) position
21782      */
21783     show : function (on_el, placement)
21784     {
21785         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21786         on_el = on_el || false; // default to false
21787          
21788         if (!on_el) {
21789             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21790                 on_el = this.parent().el;
21791             } else if (this.over) {
21792                 on_el = Roo.get(this.over);
21793             }
21794             
21795         }
21796         
21797         this.alignEl = Roo.get( on_el );
21798
21799         if (!this.el) {
21800             this.render(document.body);
21801         }
21802         
21803         
21804          
21805         
21806         if (this.title === false) {
21807             this.headerEl.hide();
21808         }
21809         
21810        
21811         this.el.show();
21812         this.el.dom.style.display = 'block';
21813          
21814         this.doAlign();
21815         
21816         //var arrow = this.el.select('.arrow',true).first();
21817         //arrow.set(align[2], 
21818         
21819         this.el.addClass('in');
21820         
21821          
21822         
21823         this.hoverState = 'in';
21824         
21825         if (this.modal) {
21826             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21827             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21828             this.maskEl.dom.style.display = 'block';
21829             this.maskEl.addClass('show');
21830         }
21831         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21832  
21833         this.fireEvent('show', this);
21834         
21835     },
21836     /**
21837      * fire this manually after loading a grid in the table for example
21838      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21839      * @param {Boolean} try and move it if we cant get right position.
21840      */
21841     updatePosition : function(placement, try_move)
21842     {
21843         // allow for calling with no parameters
21844         placement = placement   ? placement :  this.placement;
21845         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21846         
21847         this.el.removeClass([
21848             'fade','top','bottom', 'left', 'right','in',
21849             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21850         ]);
21851         this.el.addClass(placement + ' bs-popover-' + placement);
21852         
21853         if (!this.alignEl ) {
21854             return false;
21855         }
21856         
21857         switch (placement) {
21858             case 'right':
21859                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21860                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21861                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21862                     //normal display... or moved up/down.
21863                     this.el.setXY(offset);
21864                     var xy = this.alignEl.getAnchorXY('tr', false);
21865                     xy[0]+=2;xy[1]+=5;
21866                     this.arrowEl.setXY(xy);
21867                     return true;
21868                 }
21869                 // continue through...
21870                 return this.updatePosition('left', false);
21871                 
21872             
21873             case 'left':
21874                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21875                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21876                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21877                     //normal display... or moved up/down.
21878                     this.el.setXY(offset);
21879                     var xy = this.alignEl.getAnchorXY('tl', false);
21880                     xy[0]-=10;xy[1]+=5; // << fix me
21881                     this.arrowEl.setXY(xy);
21882                     return true;
21883                 }
21884                 // call self...
21885                 return this.updatePosition('right', false);
21886             
21887             case 'top':
21888                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21889                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21890                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21891                     //normal display... or moved up/down.
21892                     this.el.setXY(offset);
21893                     var xy = this.alignEl.getAnchorXY('t', false);
21894                     xy[1]-=10; // << fix me
21895                     this.arrowEl.setXY(xy);
21896                     return true;
21897                 }
21898                 // fall through
21899                return this.updatePosition('bottom', false);
21900             
21901             case 'bottom':
21902                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21903                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21904                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21905                     //normal display... or moved up/down.
21906                     this.el.setXY(offset);
21907                     var xy = this.alignEl.getAnchorXY('b', false);
21908                      xy[1]+=2; // << fix me
21909                     this.arrowEl.setXY(xy);
21910                     return true;
21911                 }
21912                 // fall through
21913                 return this.updatePosition('top', false);
21914                 
21915             
21916         }
21917         
21918         
21919         return false;
21920     },
21921     
21922     hide : function()
21923     {
21924         this.el.setXY([0,0]);
21925         this.el.removeClass('in');
21926         this.el.hide();
21927         this.hoverState = null;
21928         this.maskEl.hide(); // always..
21929         this.fireEvent('hide', this);
21930     }
21931     
21932 });
21933
21934
21935 Roo.apply(Roo.bootstrap.Popover, {
21936
21937     alignment : {
21938         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21939         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21940         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21941         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21942     },
21943     
21944     zIndex : 20001,
21945
21946     clickHander : false,
21947     
21948     
21949
21950     onMouseDown : function(e)
21951     {
21952         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21953             /// what is nothing is showing..
21954             this.hideAll();
21955         }
21956          
21957     },
21958     
21959     
21960     popups : [],
21961     
21962     register : function(popup)
21963     {
21964         if (!Roo.bootstrap.Popover.clickHandler) {
21965             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21966         }
21967         // hide other popups.
21968         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21969         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21970         this.hideAll(); //<< why?
21971         //this.popups.push(popup);
21972     },
21973     hideAll : function()
21974     {
21975         this.popups.forEach(function(p) {
21976             p.hide();
21977         });
21978     },
21979     onShow : function() {
21980         Roo.bootstrap.Popover.popups.push(this);
21981     },
21982     onHide : function() {
21983         Roo.bootstrap.Popover.popups.remove(this);
21984     } 
21985
21986 });
21987 /**
21988  * @class Roo.bootstrap.PopoverNav
21989  * @extends Roo.bootstrap.nav.Simplebar
21990  * @parent Roo.bootstrap.Popover
21991  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21992  * @licence LGPL
21993  * Bootstrap Popover header navigation class
21994  * FIXME? should this go under nav?
21995  *
21996  * 
21997  * @constructor
21998  * Create a new Popover Header Navigation 
21999  * @param {Object} config The config object
22000  */
22001
22002 Roo.bootstrap.PopoverNav = function(config){
22003     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22004 };
22005
22006 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22007     
22008     
22009     container_method : 'getPopoverHeader' 
22010     
22011      
22012     
22013     
22014    
22015 });
22016
22017  
22018
22019  /*
22020  * - LGPL
22021  *
22022  * Progress
22023  * 
22024  */
22025
22026 /**
22027  * @class Roo.bootstrap.Progress
22028  * @extends Roo.bootstrap.Component
22029  * @children Roo.bootstrap.ProgressBar
22030  * Bootstrap Progress class
22031  * @cfg {Boolean} striped striped of the progress bar
22032  * @cfg {Boolean} active animated of the progress bar
22033  * 
22034  * 
22035  * @constructor
22036  * Create a new Progress
22037  * @param {Object} config The config object
22038  */
22039
22040 Roo.bootstrap.Progress = function(config){
22041     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22042 };
22043
22044 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22045     
22046     striped : false,
22047     active: false,
22048     
22049     getAutoCreate : function(){
22050         var cfg = {
22051             tag: 'div',
22052             cls: 'progress'
22053         };
22054         
22055         
22056         if(this.striped){
22057             cfg.cls += ' progress-striped';
22058         }
22059       
22060         if(this.active){
22061             cfg.cls += ' active';
22062         }
22063         
22064         
22065         return cfg;
22066     }
22067    
22068 });
22069
22070  
22071
22072  /*
22073  * - LGPL
22074  *
22075  * ProgressBar
22076  * 
22077  */
22078
22079 /**
22080  * @class Roo.bootstrap.ProgressBar
22081  * @extends Roo.bootstrap.Component
22082  * Bootstrap ProgressBar class
22083  * @cfg {Number} aria_valuenow aria-value now
22084  * @cfg {Number} aria_valuemin aria-value min
22085  * @cfg {Number} aria_valuemax aria-value max
22086  * @cfg {String} label label for the progress bar
22087  * @cfg {String} panel (success | info | warning | danger )
22088  * @cfg {String} role role of the progress bar
22089  * @cfg {String} sr_only text
22090  * 
22091  * 
22092  * @constructor
22093  * Create a new ProgressBar
22094  * @param {Object} config The config object
22095  */
22096
22097 Roo.bootstrap.ProgressBar = function(config){
22098     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22099 };
22100
22101 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22102     
22103     aria_valuenow : 0,
22104     aria_valuemin : 0,
22105     aria_valuemax : 100,
22106     label : false,
22107     panel : false,
22108     role : false,
22109     sr_only: false,
22110     
22111     getAutoCreate : function()
22112     {
22113         
22114         var cfg = {
22115             tag: 'div',
22116             cls: 'progress-bar',
22117             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22118         };
22119         
22120         if(this.sr_only){
22121             cfg.cn = {
22122                 tag: 'span',
22123                 cls: 'sr-only',
22124                 html: this.sr_only
22125             }
22126         }
22127         
22128         if(this.role){
22129             cfg.role = this.role;
22130         }
22131         
22132         if(this.aria_valuenow){
22133             cfg['aria-valuenow'] = this.aria_valuenow;
22134         }
22135         
22136         if(this.aria_valuemin){
22137             cfg['aria-valuemin'] = this.aria_valuemin;
22138         }
22139         
22140         if(this.aria_valuemax){
22141             cfg['aria-valuemax'] = this.aria_valuemax;
22142         }
22143         
22144         if(this.label && !this.sr_only){
22145             cfg.html = this.label;
22146         }
22147         
22148         if(this.panel){
22149             cfg.cls += ' progress-bar-' + this.panel;
22150         }
22151         
22152         return cfg;
22153     },
22154     
22155     update : function(aria_valuenow)
22156     {
22157         this.aria_valuenow = aria_valuenow;
22158         
22159         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22160     }
22161    
22162 });
22163
22164  
22165
22166  /**
22167  * @class Roo.bootstrap.TabGroup
22168  * @extends Roo.bootstrap.Column
22169  * @children Roo.bootstrap.TabPanel
22170  * Bootstrap Column class
22171  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22172  * @cfg {Boolean} carousel true to make the group behave like a carousel
22173  * @cfg {Boolean} bullets show bullets for the panels
22174  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22175  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22176  * @cfg {Boolean} showarrow (true|false) show arrow default true
22177  * 
22178  * @constructor
22179  * Create a new TabGroup
22180  * @param {Object} config The config object
22181  */
22182
22183 Roo.bootstrap.TabGroup = function(config){
22184     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22185     if (!this.navId) {
22186         this.navId = Roo.id();
22187     }
22188     this.tabs = [];
22189     Roo.bootstrap.TabGroup.register(this);
22190     
22191 };
22192
22193 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22194     
22195     carousel : false,
22196     transition : false,
22197     bullets : 0,
22198     timer : 0,
22199     autoslide : false,
22200     slideFn : false,
22201     slideOnTouch : false,
22202     showarrow : true,
22203     
22204     getAutoCreate : function()
22205     {
22206         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22207         
22208         cfg.cls += ' tab-content';
22209         
22210         if (this.carousel) {
22211             cfg.cls += ' carousel slide';
22212             
22213             cfg.cn = [{
22214                cls : 'carousel-inner',
22215                cn : []
22216             }];
22217         
22218             if(this.bullets  && !Roo.isTouch){
22219                 
22220                 var bullets = {
22221                     cls : 'carousel-bullets',
22222                     cn : []
22223                 };
22224                
22225                 if(this.bullets_cls){
22226                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22227                 }
22228                 
22229                 bullets.cn.push({
22230                     cls : 'clear'
22231                 });
22232                 
22233                 cfg.cn[0].cn.push(bullets);
22234             }
22235             
22236             if(this.showarrow){
22237                 cfg.cn[0].cn.push({
22238                     tag : 'div',
22239                     class : 'carousel-arrow',
22240                     cn : [
22241                         {
22242                             tag : 'div',
22243                             class : 'carousel-prev',
22244                             cn : [
22245                                 {
22246                                     tag : 'i',
22247                                     class : 'fa fa-chevron-left'
22248                                 }
22249                             ]
22250                         },
22251                         {
22252                             tag : 'div',
22253                             class : 'carousel-next',
22254                             cn : [
22255                                 {
22256                                     tag : 'i',
22257                                     class : 'fa fa-chevron-right'
22258                                 }
22259                             ]
22260                         }
22261                     ]
22262                 });
22263             }
22264             
22265         }
22266         
22267         return cfg;
22268     },
22269     
22270     initEvents:  function()
22271     {
22272 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22273 //            this.el.on("touchstart", this.onTouchStart, this);
22274 //        }
22275         
22276         if(this.autoslide){
22277             var _this = this;
22278             
22279             this.slideFn = window.setInterval(function() {
22280                 _this.showPanelNext();
22281             }, this.timer);
22282         }
22283         
22284         if(this.showarrow){
22285             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22286             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22287         }
22288         
22289         
22290     },
22291     
22292 //    onTouchStart : function(e, el, o)
22293 //    {
22294 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22295 //            return;
22296 //        }
22297 //        
22298 //        this.showPanelNext();
22299 //    },
22300     
22301     
22302     getChildContainer : function()
22303     {
22304         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22305     },
22306     
22307     /**
22308     * register a Navigation item
22309     * @param {Roo.bootstrap.nav.Item} the navitem to add
22310     */
22311     register : function(item)
22312     {
22313         this.tabs.push( item);
22314         item.navId = this.navId; // not really needed..
22315         this.addBullet();
22316     
22317     },
22318     
22319     getActivePanel : function()
22320     {
22321         var r = false;
22322         Roo.each(this.tabs, function(t) {
22323             if (t.active) {
22324                 r = t;
22325                 return false;
22326             }
22327             return null;
22328         });
22329         return r;
22330         
22331     },
22332     getPanelByName : function(n)
22333     {
22334         var r = false;
22335         Roo.each(this.tabs, function(t) {
22336             if (t.tabId == n) {
22337                 r = t;
22338                 return false;
22339             }
22340             return null;
22341         });
22342         return r;
22343     },
22344     indexOfPanel : function(p)
22345     {
22346         var r = false;
22347         Roo.each(this.tabs, function(t,i) {
22348             if (t.tabId == p.tabId) {
22349                 r = i;
22350                 return false;
22351             }
22352             return null;
22353         });
22354         return r;
22355     },
22356     /**
22357      * show a specific panel
22358      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22359      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22360      */
22361     showPanel : function (pan)
22362     {
22363         if(this.transition || typeof(pan) == 'undefined'){
22364             Roo.log("waiting for the transitionend");
22365             return false;
22366         }
22367         
22368         if (typeof(pan) == 'number') {
22369             pan = this.tabs[pan];
22370         }
22371         
22372         if (typeof(pan) == 'string') {
22373             pan = this.getPanelByName(pan);
22374         }
22375         
22376         var cur = this.getActivePanel();
22377         
22378         if(!pan || !cur){
22379             Roo.log('pan or acitve pan is undefined');
22380             return false;
22381         }
22382         
22383         if (pan.tabId == this.getActivePanel().tabId) {
22384             return true;
22385         }
22386         
22387         if (false === cur.fireEvent('beforedeactivate')) {
22388             return false;
22389         }
22390         
22391         if(this.bullets > 0 && !Roo.isTouch){
22392             this.setActiveBullet(this.indexOfPanel(pan));
22393         }
22394         
22395         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22396             
22397             //class="carousel-item carousel-item-next carousel-item-left"
22398             
22399             this.transition = true;
22400             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22401             var lr = dir == 'next' ? 'left' : 'right';
22402             pan.el.addClass(dir); // or prev
22403             pan.el.addClass('carousel-item-' + dir); // or prev
22404             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22405             cur.el.addClass(lr); // or right
22406             pan.el.addClass(lr);
22407             cur.el.addClass('carousel-item-' +lr); // or right
22408             pan.el.addClass('carousel-item-' +lr);
22409             
22410             
22411             var _this = this;
22412             cur.el.on('transitionend', function() {
22413                 Roo.log("trans end?");
22414                 
22415                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22416                 pan.setActive(true);
22417                 
22418                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22419                 cur.setActive(false);
22420                 
22421                 _this.transition = false;
22422                 
22423             }, this, { single:  true } );
22424             
22425             return true;
22426         }
22427         
22428         cur.setActive(false);
22429         pan.setActive(true);
22430         
22431         return true;
22432         
22433     },
22434     showPanelNext : function()
22435     {
22436         var i = this.indexOfPanel(this.getActivePanel());
22437         
22438         if (i >= this.tabs.length - 1 && !this.autoslide) {
22439             return;
22440         }
22441         
22442         if (i >= this.tabs.length - 1 && this.autoslide) {
22443             i = -1;
22444         }
22445         
22446         this.showPanel(this.tabs[i+1]);
22447     },
22448     
22449     showPanelPrev : function()
22450     {
22451         var i = this.indexOfPanel(this.getActivePanel());
22452         
22453         if (i  < 1 && !this.autoslide) {
22454             return;
22455         }
22456         
22457         if (i < 1 && this.autoslide) {
22458             i = this.tabs.length;
22459         }
22460         
22461         this.showPanel(this.tabs[i-1]);
22462     },
22463     
22464     
22465     addBullet: function()
22466     {
22467         if(!this.bullets || Roo.isTouch){
22468             return;
22469         }
22470         var ctr = this.el.select('.carousel-bullets',true).first();
22471         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22472         var bullet = ctr.createChild({
22473             cls : 'bullet bullet-' + i
22474         },ctr.dom.lastChild);
22475         
22476         
22477         var _this = this;
22478         
22479         bullet.on('click', (function(e, el, o, ii, t){
22480
22481             e.preventDefault();
22482
22483             this.showPanel(ii);
22484
22485             if(this.autoslide && this.slideFn){
22486                 clearInterval(this.slideFn);
22487                 this.slideFn = window.setInterval(function() {
22488                     _this.showPanelNext();
22489                 }, this.timer);
22490             }
22491
22492         }).createDelegate(this, [i, bullet], true));
22493                 
22494         
22495     },
22496      
22497     setActiveBullet : function(i)
22498     {
22499         if(Roo.isTouch){
22500             return;
22501         }
22502         
22503         Roo.each(this.el.select('.bullet', true).elements, function(el){
22504             el.removeClass('selected');
22505         });
22506
22507         var bullet = this.el.select('.bullet-' + i, true).first();
22508         
22509         if(!bullet){
22510             return;
22511         }
22512         
22513         bullet.addClass('selected');
22514     }
22515     
22516     
22517   
22518 });
22519
22520  
22521
22522  
22523  
22524 Roo.apply(Roo.bootstrap.TabGroup, {
22525     
22526     groups: {},
22527      /**
22528     * register a Navigation Group
22529     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22530     */
22531     register : function(navgrp)
22532     {
22533         this.groups[navgrp.navId] = navgrp;
22534         
22535     },
22536     /**
22537     * fetch a Navigation Group based on the navigation ID
22538     * if one does not exist , it will get created.
22539     * @param {string} the navgroup to add
22540     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22541     */
22542     get: function(navId) {
22543         if (typeof(this.groups[navId]) == 'undefined') {
22544             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22545         }
22546         return this.groups[navId] ;
22547     }
22548     
22549     
22550     
22551 });
22552
22553  /*
22554  * - LGPL
22555  *
22556  * TabPanel
22557  * 
22558  */
22559
22560 /**
22561  * @class Roo.bootstrap.TabPanel
22562  * @extends Roo.bootstrap.Component
22563  * @children Roo.bootstrap.Component
22564  * Bootstrap TabPanel class
22565  * @cfg {Boolean} active panel active
22566  * @cfg {String} html panel content
22567  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22568  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22569  * @cfg {String} href click to link..
22570  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22571  * 
22572  * 
22573  * @constructor
22574  * Create a new TabPanel
22575  * @param {Object} config The config object
22576  */
22577
22578 Roo.bootstrap.TabPanel = function(config){
22579     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22580     this.addEvents({
22581         /**
22582              * @event changed
22583              * Fires when the active status changes
22584              * @param {Roo.bootstrap.TabPanel} this
22585              * @param {Boolean} state the new state
22586             
22587          */
22588         'changed': true,
22589         /**
22590              * @event beforedeactivate
22591              * Fires before a tab is de-activated - can be used to do validation on a form.
22592              * @param {Roo.bootstrap.TabPanel} this
22593              * @return {Boolean} false if there is an error
22594             
22595          */
22596         'beforedeactivate': true
22597      });
22598     
22599     this.tabId = this.tabId || Roo.id();
22600   
22601 };
22602
22603 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22604     
22605     active: false,
22606     html: false,
22607     tabId: false,
22608     navId : false,
22609     href : '',
22610     touchSlide : false,
22611     getAutoCreate : function(){
22612         
22613         
22614         var cfg = {
22615             tag: 'div',
22616             // item is needed for carousel - not sure if it has any effect otherwise
22617             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22618             html: this.html || ''
22619         };
22620         
22621         if(this.active){
22622             cfg.cls += ' active';
22623         }
22624         
22625         if(this.tabId){
22626             cfg.tabId = this.tabId;
22627         }
22628         
22629         
22630         
22631         return cfg;
22632     },
22633     
22634     initEvents:  function()
22635     {
22636         var p = this.parent();
22637         
22638         this.navId = this.navId || p.navId;
22639         
22640         if (typeof(this.navId) != 'undefined') {
22641             // not really needed.. but just in case.. parent should be a NavGroup.
22642             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22643             
22644             tg.register(this);
22645             
22646             var i = tg.tabs.length - 1;
22647             
22648             if(this.active && tg.bullets > 0 && i < tg.bullets){
22649                 tg.setActiveBullet(i);
22650             }
22651         }
22652         
22653         this.el.on('click', this.onClick, this);
22654         
22655         if(Roo.isTouch && this.touchSlide){
22656             this.el.on("touchstart", this.onTouchStart, this);
22657             this.el.on("touchmove", this.onTouchMove, this);
22658             this.el.on("touchend", this.onTouchEnd, this);
22659         }
22660         
22661     },
22662     
22663     onRender : function(ct, position)
22664     {
22665         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22666     },
22667     
22668     setActive : function(state)
22669     {
22670         Roo.log("panel - set active " + this.tabId + "=" + state);
22671         
22672         this.active = state;
22673         if (!state) {
22674             this.el.removeClass('active');
22675             
22676         } else  if (!this.el.hasClass('active')) {
22677             this.el.addClass('active');
22678         }
22679         
22680         this.fireEvent('changed', this, state);
22681     },
22682     
22683     onClick : function(e)
22684     {
22685         e.preventDefault();
22686         
22687         if(!this.href.length){
22688             return;
22689         }
22690         
22691         window.location.href = this.href;
22692     },
22693     
22694     startX : 0,
22695     startY : 0,
22696     endX : 0,
22697     endY : 0,
22698     swiping : false,
22699     
22700     onTouchStart : function(e)
22701     {
22702         this.swiping = false;
22703         
22704         this.startX = e.browserEvent.touches[0].clientX;
22705         this.startY = e.browserEvent.touches[0].clientY;
22706     },
22707     
22708     onTouchMove : function(e)
22709     {
22710         this.swiping = true;
22711         
22712         this.endX = e.browserEvent.touches[0].clientX;
22713         this.endY = e.browserEvent.touches[0].clientY;
22714     },
22715     
22716     onTouchEnd : function(e)
22717     {
22718         if(!this.swiping){
22719             this.onClick(e);
22720             return;
22721         }
22722         
22723         var tabGroup = this.parent();
22724         
22725         if(this.endX > this.startX){ // swiping right
22726             tabGroup.showPanelPrev();
22727             return;
22728         }
22729         
22730         if(this.startX > this.endX){ // swiping left
22731             tabGroup.showPanelNext();
22732             return;
22733         }
22734     }
22735     
22736     
22737 });
22738  
22739
22740  
22741
22742  /*
22743  * - LGPL
22744  *
22745  * DateField
22746  * 
22747  */
22748
22749 /**
22750  * @class Roo.bootstrap.form.DateField
22751  * @extends Roo.bootstrap.form.Input
22752  * Bootstrap DateField class
22753  * @cfg {Number} weekStart default 0
22754  * @cfg {String} viewMode default empty, (months|years)
22755  * @cfg {String} minViewMode default empty, (months|years)
22756  * @cfg {Number} startDate default -Infinity
22757  * @cfg {Number} endDate default Infinity
22758  * @cfg {Boolean} todayHighlight default false
22759  * @cfg {Boolean} todayBtn default false
22760  * @cfg {Boolean} calendarWeeks default false
22761  * @cfg {Object} daysOfWeekDisabled default empty
22762  * @cfg {Boolean} singleMode default false (true | false)
22763  * 
22764  * @cfg {Boolean} keyboardNavigation default true
22765  * @cfg {String} language default en
22766  * 
22767  * @constructor
22768  * Create a new DateField
22769  * @param {Object} config The config object
22770  */
22771
22772 Roo.bootstrap.form.DateField = function(config){
22773     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22774      this.addEvents({
22775             /**
22776              * @event show
22777              * Fires when this field show.
22778              * @param {Roo.bootstrap.form.DateField} this
22779              * @param {Mixed} date The date value
22780              */
22781             show : true,
22782             /**
22783              * @event show
22784              * Fires when this field hide.
22785              * @param {Roo.bootstrap.form.DateField} this
22786              * @param {Mixed} date The date value
22787              */
22788             hide : true,
22789             /**
22790              * @event select
22791              * Fires when select a date.
22792              * @param {Roo.bootstrap.form.DateField} this
22793              * @param {Mixed} date The date value
22794              */
22795             select : true,
22796             /**
22797              * @event beforeselect
22798              * Fires when before select a date.
22799              * @param {Roo.bootstrap.form.DateField} this
22800              * @param {Mixed} date The date value
22801              */
22802             beforeselect : true
22803         });
22804 };
22805
22806 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22807     
22808     /**
22809      * @cfg {String} format
22810      * The default date format string which can be overriden for localization support.  The format must be
22811      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22812      */
22813     format : "m/d/y",
22814     /**
22815      * @cfg {String} altFormats
22816      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22817      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22818      */
22819     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22820     
22821     weekStart : 0,
22822     
22823     viewMode : '',
22824     
22825     minViewMode : '',
22826     
22827     todayHighlight : false,
22828     
22829     todayBtn: false,
22830     
22831     language: 'en',
22832     
22833     keyboardNavigation: true,
22834     
22835     calendarWeeks: false,
22836     
22837     startDate: -Infinity,
22838     
22839     endDate: Infinity,
22840     
22841     daysOfWeekDisabled: [],
22842     
22843     _events: [],
22844     
22845     singleMode : false,
22846     
22847     UTCDate: function()
22848     {
22849         return new Date(Date.UTC.apply(Date, arguments));
22850     },
22851     
22852     UTCToday: function()
22853     {
22854         var today = new Date();
22855         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22856     },
22857     
22858     getDate: function() {
22859             var d = this.getUTCDate();
22860             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22861     },
22862     
22863     getUTCDate: function() {
22864             return this.date;
22865     },
22866     
22867     setDate: function(d) {
22868             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22869     },
22870     
22871     setUTCDate: function(d) {
22872             this.date = d;
22873             this.setValue(this.formatDate(this.date));
22874     },
22875         
22876     onRender: function(ct, position)
22877     {
22878         
22879         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22880         
22881         this.language = this.language || 'en';
22882         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22883         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22884         
22885         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22886         this.format = this.format || 'm/d/y';
22887         this.isInline = false;
22888         this.isInput = true;
22889         this.component = this.el.select('.add-on', true).first() || false;
22890         this.component = (this.component && this.component.length === 0) ? false : this.component;
22891         this.hasInput = this.component && this.inputEl().length;
22892         
22893         if (typeof(this.minViewMode === 'string')) {
22894             switch (this.minViewMode) {
22895                 case 'months':
22896                     this.minViewMode = 1;
22897                     break;
22898                 case 'years':
22899                     this.minViewMode = 2;
22900                     break;
22901                 default:
22902                     this.minViewMode = 0;
22903                     break;
22904             }
22905         }
22906         
22907         if (typeof(this.viewMode === 'string')) {
22908             switch (this.viewMode) {
22909                 case 'months':
22910                     this.viewMode = 1;
22911                     break;
22912                 case 'years':
22913                     this.viewMode = 2;
22914                     break;
22915                 default:
22916                     this.viewMode = 0;
22917                     break;
22918             }
22919         }
22920                 
22921         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22922         
22923 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22924         
22925         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22926         
22927         this.picker().on('mousedown', this.onMousedown, this);
22928         this.picker().on('click', this.onClick, this);
22929         
22930         this.picker().addClass('datepicker-dropdown');
22931         
22932         this.startViewMode = this.viewMode;
22933         
22934         if(this.singleMode){
22935             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22936                 v.setVisibilityMode(Roo.Element.DISPLAY);
22937                 v.hide();
22938             });
22939             
22940             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22941                 v.setStyle('width', '189px');
22942             });
22943         }
22944         
22945         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22946             if(!this.calendarWeeks){
22947                 v.remove();
22948                 return;
22949             }
22950             
22951             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22952             v.attr('colspan', function(i, val){
22953                 return parseInt(val) + 1;
22954             });
22955         });
22956                         
22957         
22958         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22959         
22960         this.setStartDate(this.startDate);
22961         this.setEndDate(this.endDate);
22962         
22963         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22964         
22965         this.fillDow();
22966         this.fillMonths();
22967         this.update();
22968         this.showMode();
22969         
22970         if(this.isInline) {
22971             this.showPopup();
22972         }
22973     },
22974     
22975     picker : function()
22976     {
22977         return this.pickerEl;
22978 //        return this.el.select('.datepicker', true).first();
22979     },
22980     
22981     fillDow: function()
22982     {
22983         var dowCnt = this.weekStart;
22984         
22985         var dow = {
22986             tag: 'tr',
22987             cn: [
22988                 
22989             ]
22990         };
22991         
22992         if(this.calendarWeeks){
22993             dow.cn.push({
22994                 tag: 'th',
22995                 cls: 'cw',
22996                 html: '&nbsp;'
22997             })
22998         }
22999         
23000         while (dowCnt < this.weekStart + 7) {
23001             dow.cn.push({
23002                 tag: 'th',
23003                 cls: 'dow',
23004                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23005             });
23006         }
23007         
23008         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23009     },
23010     
23011     fillMonths: function()
23012     {    
23013         var i = 0;
23014         var months = this.picker().select('>.datepicker-months td', true).first();
23015         
23016         months.dom.innerHTML = '';
23017         
23018         while (i < 12) {
23019             var month = {
23020                 tag: 'span',
23021                 cls: 'month',
23022                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23023             };
23024             
23025             months.createChild(month);
23026         }
23027         
23028     },
23029     
23030     update: function()
23031     {
23032         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;
23033         
23034         if (this.date < this.startDate) {
23035             this.viewDate = new Date(this.startDate);
23036         } else if (this.date > this.endDate) {
23037             this.viewDate = new Date(this.endDate);
23038         } else {
23039             this.viewDate = new Date(this.date);
23040         }
23041         
23042         this.fill();
23043     },
23044     
23045     fill: function() 
23046     {
23047         var d = new Date(this.viewDate),
23048                 year = d.getUTCFullYear(),
23049                 month = d.getUTCMonth(),
23050                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23051                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23052                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23053                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23054                 currentDate = this.date && this.date.valueOf(),
23055                 today = this.UTCToday();
23056         
23057         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23058         
23059 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23060         
23061 //        this.picker.select('>tfoot th.today').
23062 //                                              .text(dates[this.language].today)
23063 //                                              .toggle(this.todayBtn !== false);
23064     
23065         this.updateNavArrows();
23066         this.fillMonths();
23067                                                 
23068         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23069         
23070         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23071          
23072         prevMonth.setUTCDate(day);
23073         
23074         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23075         
23076         var nextMonth = new Date(prevMonth);
23077         
23078         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23079         
23080         nextMonth = nextMonth.valueOf();
23081         
23082         var fillMonths = false;
23083         
23084         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23085         
23086         while(prevMonth.valueOf() <= nextMonth) {
23087             var clsName = '';
23088             
23089             if (prevMonth.getUTCDay() === this.weekStart) {
23090                 if(fillMonths){
23091                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23092                 }
23093                     
23094                 fillMonths = {
23095                     tag: 'tr',
23096                     cn: []
23097                 };
23098                 
23099                 if(this.calendarWeeks){
23100                     // ISO 8601: First week contains first thursday.
23101                     // ISO also states week starts on Monday, but we can be more abstract here.
23102                     var
23103                     // Start of current week: based on weekstart/current date
23104                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23105                     // Thursday of this week
23106                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23107                     // First Thursday of year, year from thursday
23108                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23109                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23110                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23111                     
23112                     fillMonths.cn.push({
23113                         tag: 'td',
23114                         cls: 'cw',
23115                         html: calWeek
23116                     });
23117                 }
23118             }
23119             
23120             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23121                 clsName += ' old';
23122             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23123                 clsName += ' new';
23124             }
23125             if (this.todayHighlight &&
23126                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23127                 prevMonth.getUTCMonth() == today.getMonth() &&
23128                 prevMonth.getUTCDate() == today.getDate()) {
23129                 clsName += ' today';
23130             }
23131             
23132             if (currentDate && prevMonth.valueOf() === currentDate) {
23133                 clsName += ' active';
23134             }
23135             
23136             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23137                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23138                     clsName += ' disabled';
23139             }
23140             
23141             fillMonths.cn.push({
23142                 tag: 'td',
23143                 cls: 'day ' + clsName,
23144                 html: prevMonth.getDate()
23145             });
23146             
23147             prevMonth.setDate(prevMonth.getDate()+1);
23148         }
23149           
23150         var currentYear = this.date && this.date.getUTCFullYear();
23151         var currentMonth = this.date && this.date.getUTCMonth();
23152         
23153         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23154         
23155         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23156             v.removeClass('active');
23157             
23158             if(currentYear === year && k === currentMonth){
23159                 v.addClass('active');
23160             }
23161             
23162             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23163                 v.addClass('disabled');
23164             }
23165             
23166         });
23167         
23168         
23169         year = parseInt(year/10, 10) * 10;
23170         
23171         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23172         
23173         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23174         
23175         year -= 1;
23176         for (var i = -1; i < 11; i++) {
23177             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23178                 tag: 'span',
23179                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23180                 html: year
23181             });
23182             
23183             year += 1;
23184         }
23185     },
23186     
23187     showMode: function(dir) 
23188     {
23189         if (dir) {
23190             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23191         }
23192         
23193         Roo.each(this.picker().select('>div',true).elements, function(v){
23194             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23195             v.hide();
23196         });
23197         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23198     },
23199     
23200     place: function()
23201     {
23202         if(this.isInline) {
23203             return;
23204         }
23205         
23206         this.picker().removeClass(['bottom', 'top']);
23207         
23208         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23209             /*
23210              * place to the top of element!
23211              *
23212              */
23213             
23214             this.picker().addClass('top');
23215             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23216             
23217             return;
23218         }
23219         
23220         this.picker().addClass('bottom');
23221         
23222         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23223     },
23224     
23225     parseDate : function(value)
23226     {
23227         if(!value || value instanceof Date){
23228             return value;
23229         }
23230         var v = Date.parseDate(value, this.format);
23231         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23232             v = Date.parseDate(value, 'Y-m-d');
23233         }
23234         if(!v && this.altFormats){
23235             if(!this.altFormatsArray){
23236                 this.altFormatsArray = this.altFormats.split("|");
23237             }
23238             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23239                 v = Date.parseDate(value, this.altFormatsArray[i]);
23240             }
23241         }
23242         return v;
23243     },
23244     
23245     formatDate : function(date, fmt)
23246     {   
23247         return (!date || !(date instanceof Date)) ?
23248         date : date.dateFormat(fmt || this.format);
23249     },
23250     
23251     onFocus : function()
23252     {
23253         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23254         this.showPopup();
23255     },
23256     
23257     onBlur : function()
23258     {
23259         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23260         
23261         var d = this.inputEl().getValue();
23262         
23263         this.setValue(d);
23264                 
23265         this.hidePopup();
23266     },
23267     
23268     showPopup : function()
23269     {
23270         this.picker().show();
23271         this.update();
23272         this.place();
23273         
23274         this.fireEvent('showpopup', this, this.date);
23275     },
23276     
23277     hidePopup : function()
23278     {
23279         if(this.isInline) {
23280             return;
23281         }
23282         this.picker().hide();
23283         this.viewMode = this.startViewMode;
23284         this.showMode();
23285         
23286         this.fireEvent('hidepopup', this, this.date);
23287         
23288     },
23289     
23290     onMousedown: function(e)
23291     {
23292         e.stopPropagation();
23293         e.preventDefault();
23294     },
23295     
23296     keyup: function(e)
23297     {
23298         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23299         this.update();
23300     },
23301
23302     setValue: function(v)
23303     {
23304         if(this.fireEvent('beforeselect', this, v) !== false){
23305             var d = new Date(this.parseDate(v) ).clearTime();
23306         
23307             if(isNaN(d.getTime())){
23308                 this.date = this.viewDate = '';
23309                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23310                 return;
23311             }
23312
23313             v = this.formatDate(d);
23314
23315             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23316
23317             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23318
23319             this.update();
23320
23321             this.fireEvent('select', this, this.date);
23322         }
23323     },
23324     
23325     getValue: function()
23326     {
23327         return this.formatDate(this.date);
23328     },
23329     
23330     fireKey: function(e)
23331     {
23332         if (!this.picker().isVisible()){
23333             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23334                 this.showPopup();
23335             }
23336             return;
23337         }
23338         
23339         var dateChanged = false,
23340         dir, day, month,
23341         newDate, newViewDate;
23342         
23343         switch(e.keyCode){
23344             case 27: // escape
23345                 this.hidePopup();
23346                 e.preventDefault();
23347                 break;
23348             case 37: // left
23349             case 39: // right
23350                 if (!this.keyboardNavigation) {
23351                     break;
23352                 }
23353                 dir = e.keyCode == 37 ? -1 : 1;
23354                 
23355                 if (e.ctrlKey){
23356                     newDate = this.moveYear(this.date, dir);
23357                     newViewDate = this.moveYear(this.viewDate, dir);
23358                 } else if (e.shiftKey){
23359                     newDate = this.moveMonth(this.date, dir);
23360                     newViewDate = this.moveMonth(this.viewDate, dir);
23361                 } else {
23362                     newDate = new Date(this.date);
23363                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23364                     newViewDate = new Date(this.viewDate);
23365                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23366                 }
23367                 if (this.dateWithinRange(newDate)){
23368                     this.date = newDate;
23369                     this.viewDate = newViewDate;
23370                     this.setValue(this.formatDate(this.date));
23371 //                    this.update();
23372                     e.preventDefault();
23373                     dateChanged = true;
23374                 }
23375                 break;
23376             case 38: // up
23377             case 40: // down
23378                 if (!this.keyboardNavigation) {
23379                     break;
23380                 }
23381                 dir = e.keyCode == 38 ? -1 : 1;
23382                 if (e.ctrlKey){
23383                     newDate = this.moveYear(this.date, dir);
23384                     newViewDate = this.moveYear(this.viewDate, dir);
23385                 } else if (e.shiftKey){
23386                     newDate = this.moveMonth(this.date, dir);
23387                     newViewDate = this.moveMonth(this.viewDate, dir);
23388                 } else {
23389                     newDate = new Date(this.date);
23390                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23391                     newViewDate = new Date(this.viewDate);
23392                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23393                 }
23394                 if (this.dateWithinRange(newDate)){
23395                     this.date = newDate;
23396                     this.viewDate = newViewDate;
23397                     this.setValue(this.formatDate(this.date));
23398 //                    this.update();
23399                     e.preventDefault();
23400                     dateChanged = true;
23401                 }
23402                 break;
23403             case 13: // enter
23404                 this.setValue(this.formatDate(this.date));
23405                 this.hidePopup();
23406                 e.preventDefault();
23407                 break;
23408             case 9: // tab
23409                 this.setValue(this.formatDate(this.date));
23410                 this.hidePopup();
23411                 break;
23412             case 16: // shift
23413             case 17: // ctrl
23414             case 18: // alt
23415                 break;
23416             default :
23417                 this.hidePopup();
23418                 
23419         }
23420     },
23421     
23422     
23423     onClick: function(e) 
23424     {
23425         e.stopPropagation();
23426         e.preventDefault();
23427         
23428         var target = e.getTarget();
23429         
23430         if(target.nodeName.toLowerCase() === 'i'){
23431             target = Roo.get(target).dom.parentNode;
23432         }
23433         
23434         var nodeName = target.nodeName;
23435         var className = target.className;
23436         var html = target.innerHTML;
23437         //Roo.log(nodeName);
23438         
23439         switch(nodeName.toLowerCase()) {
23440             case 'th':
23441                 switch(className) {
23442                     case 'switch':
23443                         this.showMode(1);
23444                         break;
23445                     case 'prev':
23446                     case 'next':
23447                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23448                         switch(this.viewMode){
23449                                 case 0:
23450                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23451                                         break;
23452                                 case 1:
23453                                 case 2:
23454                                         this.viewDate = this.moveYear(this.viewDate, dir);
23455                                         break;
23456                         }
23457                         this.fill();
23458                         break;
23459                     case 'today':
23460                         var date = new Date();
23461                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23462 //                        this.fill()
23463                         this.setValue(this.formatDate(this.date));
23464                         
23465                         this.hidePopup();
23466                         break;
23467                 }
23468                 break;
23469             case 'span':
23470                 if (className.indexOf('disabled') < 0) {
23471                 if (!this.viewDate) {
23472                     this.viewDate = new Date();
23473                 }
23474                 this.viewDate.setUTCDate(1);
23475                     if (className.indexOf('month') > -1) {
23476                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23477                     } else {
23478                         var year = parseInt(html, 10) || 0;
23479                         this.viewDate.setUTCFullYear(year);
23480                         
23481                     }
23482                     
23483                     if(this.singleMode){
23484                         this.setValue(this.formatDate(this.viewDate));
23485                         this.hidePopup();
23486                         return;
23487                     }
23488                     
23489                     this.showMode(-1);
23490                     this.fill();
23491                 }
23492                 break;
23493                 
23494             case 'td':
23495                 //Roo.log(className);
23496                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23497                     var day = parseInt(html, 10) || 1;
23498                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23499                         month = (this.viewDate || new Date()).getUTCMonth();
23500
23501                     if (className.indexOf('old') > -1) {
23502                         if(month === 0 ){
23503                             month = 11;
23504                             year -= 1;
23505                         }else{
23506                             month -= 1;
23507                         }
23508                     } else if (className.indexOf('new') > -1) {
23509                         if (month == 11) {
23510                             month = 0;
23511                             year += 1;
23512                         } else {
23513                             month += 1;
23514                         }
23515                     }
23516                     //Roo.log([year,month,day]);
23517                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23518                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23519 //                    this.fill();
23520                     //Roo.log(this.formatDate(this.date));
23521                     this.setValue(this.formatDate(this.date));
23522                     this.hidePopup();
23523                 }
23524                 break;
23525         }
23526     },
23527     
23528     setStartDate: function(startDate)
23529     {
23530         this.startDate = startDate || -Infinity;
23531         if (this.startDate !== -Infinity) {
23532             this.startDate = this.parseDate(this.startDate);
23533         }
23534         this.update();
23535         this.updateNavArrows();
23536     },
23537
23538     setEndDate: function(endDate)
23539     {
23540         this.endDate = endDate || Infinity;
23541         if (this.endDate !== Infinity) {
23542             this.endDate = this.parseDate(this.endDate);
23543         }
23544         this.update();
23545         this.updateNavArrows();
23546     },
23547     
23548     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23549     {
23550         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23551         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23552             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23553         }
23554         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23555             return parseInt(d, 10);
23556         });
23557         this.update();
23558         this.updateNavArrows();
23559     },
23560     
23561     updateNavArrows: function() 
23562     {
23563         if(this.singleMode){
23564             return;
23565         }
23566         
23567         var d = new Date(this.viewDate),
23568         year = d.getUTCFullYear(),
23569         month = d.getUTCMonth();
23570         
23571         Roo.each(this.picker().select('.prev', true).elements, function(v){
23572             v.show();
23573             switch (this.viewMode) {
23574                 case 0:
23575
23576                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23577                         v.hide();
23578                     }
23579                     break;
23580                 case 1:
23581                 case 2:
23582                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23583                         v.hide();
23584                     }
23585                     break;
23586             }
23587         });
23588         
23589         Roo.each(this.picker().select('.next', true).elements, function(v){
23590             v.show();
23591             switch (this.viewMode) {
23592                 case 0:
23593
23594                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23595                         v.hide();
23596                     }
23597                     break;
23598                 case 1:
23599                 case 2:
23600                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23601                         v.hide();
23602                     }
23603                     break;
23604             }
23605         })
23606     },
23607     
23608     moveMonth: function(date, dir)
23609     {
23610         if (!dir) {
23611             return date;
23612         }
23613         var new_date = new Date(date.valueOf()),
23614         day = new_date.getUTCDate(),
23615         month = new_date.getUTCMonth(),
23616         mag = Math.abs(dir),
23617         new_month, test;
23618         dir = dir > 0 ? 1 : -1;
23619         if (mag == 1){
23620             test = dir == -1
23621             // If going back one month, make sure month is not current month
23622             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23623             ? function(){
23624                 return new_date.getUTCMonth() == month;
23625             }
23626             // If going forward one month, make sure month is as expected
23627             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23628             : function(){
23629                 return new_date.getUTCMonth() != new_month;
23630             };
23631             new_month = month + dir;
23632             new_date.setUTCMonth(new_month);
23633             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23634             if (new_month < 0 || new_month > 11) {
23635                 new_month = (new_month + 12) % 12;
23636             }
23637         } else {
23638             // For magnitudes >1, move one month at a time...
23639             for (var i=0; i<mag; i++) {
23640                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23641                 new_date = this.moveMonth(new_date, dir);
23642             }
23643             // ...then reset the day, keeping it in the new month
23644             new_month = new_date.getUTCMonth();
23645             new_date.setUTCDate(day);
23646             test = function(){
23647                 return new_month != new_date.getUTCMonth();
23648             };
23649         }
23650         // Common date-resetting loop -- if date is beyond end of month, make it
23651         // end of month
23652         while (test()){
23653             new_date.setUTCDate(--day);
23654             new_date.setUTCMonth(new_month);
23655         }
23656         return new_date;
23657     },
23658
23659     moveYear: function(date, dir)
23660     {
23661         return this.moveMonth(date, dir*12);
23662     },
23663
23664     dateWithinRange: function(date)
23665     {
23666         return date >= this.startDate && date <= this.endDate;
23667     },
23668
23669     
23670     remove: function() 
23671     {
23672         this.picker().remove();
23673     },
23674     
23675     validateValue : function(value)
23676     {
23677         if(this.getVisibilityEl().hasClass('hidden')){
23678             return true;
23679         }
23680         
23681         if(value.length < 1)  {
23682             if(this.allowBlank){
23683                 return true;
23684             }
23685             return false;
23686         }
23687         
23688         if(value.length < this.minLength){
23689             return false;
23690         }
23691         if(value.length > this.maxLength){
23692             return false;
23693         }
23694         if(this.vtype){
23695             var vt = Roo.form.VTypes;
23696             if(!vt[this.vtype](value, this)){
23697                 return false;
23698             }
23699         }
23700         if(typeof this.validator == "function"){
23701             var msg = this.validator(value);
23702             if(msg !== true){
23703                 return false;
23704             }
23705         }
23706         
23707         if(this.regex && !this.regex.test(value)){
23708             return false;
23709         }
23710         
23711         if(typeof(this.parseDate(value)) == 'undefined'){
23712             return false;
23713         }
23714         
23715         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23716             return false;
23717         }      
23718         
23719         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23720             return false;
23721         } 
23722         
23723         
23724         return true;
23725     },
23726     
23727     reset : function()
23728     {
23729         this.date = this.viewDate = '';
23730         
23731         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23732     }
23733    
23734 });
23735
23736 Roo.apply(Roo.bootstrap.form.DateField,  {
23737     
23738     head : {
23739         tag: 'thead',
23740         cn: [
23741         {
23742             tag: 'tr',
23743             cn: [
23744             {
23745                 tag: 'th',
23746                 cls: 'prev',
23747                 html: '<i class="fa fa-arrow-left"/>'
23748             },
23749             {
23750                 tag: 'th',
23751                 cls: 'switch',
23752                 colspan: '5'
23753             },
23754             {
23755                 tag: 'th',
23756                 cls: 'next',
23757                 html: '<i class="fa fa-arrow-right"/>'
23758             }
23759
23760             ]
23761         }
23762         ]
23763     },
23764     
23765     content : {
23766         tag: 'tbody',
23767         cn: [
23768         {
23769             tag: 'tr',
23770             cn: [
23771             {
23772                 tag: 'td',
23773                 colspan: '7'
23774             }
23775             ]
23776         }
23777         ]
23778     },
23779     
23780     footer : {
23781         tag: 'tfoot',
23782         cn: [
23783         {
23784             tag: 'tr',
23785             cn: [
23786             {
23787                 tag: 'th',
23788                 colspan: '7',
23789                 cls: 'today'
23790             }
23791                     
23792             ]
23793         }
23794         ]
23795     },
23796     
23797     dates:{
23798         en: {
23799             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23800             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23801             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23802             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23803             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23804             today: "Today"
23805         }
23806     },
23807     
23808     modes: [
23809     {
23810         clsName: 'days',
23811         navFnc: 'Month',
23812         navStep: 1
23813     },
23814     {
23815         clsName: 'months',
23816         navFnc: 'FullYear',
23817         navStep: 1
23818     },
23819     {
23820         clsName: 'years',
23821         navFnc: 'FullYear',
23822         navStep: 10
23823     }]
23824 });
23825
23826 Roo.apply(Roo.bootstrap.form.DateField,  {
23827   
23828     template : {
23829         tag: 'div',
23830         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23831         cn: [
23832         {
23833             tag: 'div',
23834             cls: 'datepicker-days',
23835             cn: [
23836             {
23837                 tag: 'table',
23838                 cls: 'table-condensed',
23839                 cn:[
23840                 Roo.bootstrap.form.DateField.head,
23841                 {
23842                     tag: 'tbody'
23843                 },
23844                 Roo.bootstrap.form.DateField.footer
23845                 ]
23846             }
23847             ]
23848         },
23849         {
23850             tag: 'div',
23851             cls: 'datepicker-months',
23852             cn: [
23853             {
23854                 tag: 'table',
23855                 cls: 'table-condensed',
23856                 cn:[
23857                 Roo.bootstrap.form.DateField.head,
23858                 Roo.bootstrap.form.DateField.content,
23859                 Roo.bootstrap.form.DateField.footer
23860                 ]
23861             }
23862             ]
23863         },
23864         {
23865             tag: 'div',
23866             cls: 'datepicker-years',
23867             cn: [
23868             {
23869                 tag: 'table',
23870                 cls: 'table-condensed',
23871                 cn:[
23872                 Roo.bootstrap.form.DateField.head,
23873                 Roo.bootstrap.form.DateField.content,
23874                 Roo.bootstrap.form.DateField.footer
23875                 ]
23876             }
23877             ]
23878         }
23879         ]
23880     }
23881 });
23882
23883  
23884
23885  /*
23886  * - LGPL
23887  *
23888  * TimeField
23889  * 
23890  */
23891
23892 /**
23893  * @class Roo.bootstrap.form.TimeField
23894  * @extends Roo.bootstrap.form.Input
23895  * Bootstrap DateField class
23896  * 
23897  * 
23898  * @constructor
23899  * Create a new TimeField
23900  * @param {Object} config The config object
23901  */
23902
23903 Roo.bootstrap.form.TimeField = function(config){
23904     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23905     this.addEvents({
23906             /**
23907              * @event show
23908              * Fires when this field show.
23909              * @param {Roo.bootstrap.form.DateField} thisthis
23910              * @param {Mixed} date The date value
23911              */
23912             show : true,
23913             /**
23914              * @event show
23915              * Fires when this field hide.
23916              * @param {Roo.bootstrap.form.DateField} this
23917              * @param {Mixed} date The date value
23918              */
23919             hide : true,
23920             /**
23921              * @event select
23922              * Fires when select a date.
23923              * @param {Roo.bootstrap.form.DateField} this
23924              * @param {Mixed} date The date value
23925              */
23926             select : true
23927         });
23928 };
23929
23930 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23931     
23932     /**
23933      * @cfg {String} format
23934      * The default time format string which can be overriden for localization support.  The format must be
23935      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23936      */
23937     format : "H:i",
23938
23939     getAutoCreate : function()
23940     {
23941         this.after = '<i class="fa far fa-clock"></i>';
23942         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23943         
23944          
23945     },
23946     onRender: function(ct, position)
23947     {
23948         
23949         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23950                 
23951         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23952         
23953         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23954         
23955         this.pop = this.picker().select('>.datepicker-time',true).first();
23956         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23957         
23958         this.picker().on('mousedown', this.onMousedown, this);
23959         this.picker().on('click', this.onClick, this);
23960         
23961         this.picker().addClass('datepicker-dropdown');
23962     
23963         this.fillTime();
23964         this.update();
23965             
23966         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23967         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23968         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23969         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23970         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23971         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23972
23973     },
23974     
23975     fireKey: function(e){
23976         if (!this.picker().isVisible()){
23977             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23978                 this.show();
23979             }
23980             return;
23981         }
23982
23983         e.preventDefault();
23984         
23985         switch(e.keyCode){
23986             case 27: // escape
23987                 this.hide();
23988                 break;
23989             case 37: // left
23990             case 39: // right
23991                 this.onTogglePeriod();
23992                 break;
23993             case 38: // up
23994                 this.onIncrementMinutes();
23995                 break;
23996             case 40: // down
23997                 this.onDecrementMinutes();
23998                 break;
23999             case 13: // enter
24000             case 9: // tab
24001                 this.setTime();
24002                 break;
24003         }
24004     },
24005     
24006     onClick: function(e) {
24007         e.stopPropagation();
24008         e.preventDefault();
24009     },
24010     
24011     picker : function()
24012     {
24013         return this.pickerEl;
24014     },
24015     
24016     fillTime: function()
24017     {    
24018         var time = this.pop.select('tbody', true).first();
24019         
24020         time.dom.innerHTML = '';
24021         
24022         time.createChild({
24023             tag: 'tr',
24024             cn: [
24025                 {
24026                     tag: 'td',
24027                     cn: [
24028                         {
24029                             tag: 'a',
24030                             href: '#',
24031                             cls: 'btn',
24032                             cn: [
24033                                 {
24034                                     tag: 'i',
24035                                     cls: 'hours-up fa fas fa-chevron-up'
24036                                 }
24037                             ]
24038                         } 
24039                     ]
24040                 },
24041                 {
24042                     tag: 'td',
24043                     cls: 'separator'
24044                 },
24045                 {
24046                     tag: 'td',
24047                     cn: [
24048                         {
24049                             tag: 'a',
24050                             href: '#',
24051                             cls: 'btn',
24052                             cn: [
24053                                 {
24054                                     tag: 'i',
24055                                     cls: 'minutes-up fa fas fa-chevron-up'
24056                                 }
24057                             ]
24058                         }
24059                     ]
24060                 },
24061                 {
24062                     tag: 'td',
24063                     cls: 'separator'
24064                 }
24065             ]
24066         });
24067         
24068         time.createChild({
24069             tag: 'tr',
24070             cn: [
24071                 {
24072                     tag: 'td',
24073                     cn: [
24074                         {
24075                             tag: 'span',
24076                             cls: 'timepicker-hour',
24077                             html: '00'
24078                         }  
24079                     ]
24080                 },
24081                 {
24082                     tag: 'td',
24083                     cls: 'separator',
24084                     html: ':'
24085                 },
24086                 {
24087                     tag: 'td',
24088                     cn: [
24089                         {
24090                             tag: 'span',
24091                             cls: 'timepicker-minute',
24092                             html: '00'
24093                         }  
24094                     ]
24095                 },
24096                 {
24097                     tag: 'td',
24098                     cls: 'separator'
24099                 },
24100                 {
24101                     tag: 'td',
24102                     cn: [
24103                         {
24104                             tag: 'button',
24105                             type: 'button',
24106                             cls: 'btn btn-primary period',
24107                             html: 'AM'
24108                             
24109                         }
24110                     ]
24111                 }
24112             ]
24113         });
24114         
24115         time.createChild({
24116             tag: 'tr',
24117             cn: [
24118                 {
24119                     tag: 'td',
24120                     cn: [
24121                         {
24122                             tag: 'a',
24123                             href: '#',
24124                             cls: 'btn',
24125                             cn: [
24126                                 {
24127                                     tag: 'span',
24128                                     cls: 'hours-down fa fas fa-chevron-down'
24129                                 }
24130                             ]
24131                         }
24132                     ]
24133                 },
24134                 {
24135                     tag: 'td',
24136                     cls: 'separator'
24137                 },
24138                 {
24139                     tag: 'td',
24140                     cn: [
24141                         {
24142                             tag: 'a',
24143                             href: '#',
24144                             cls: 'btn',
24145                             cn: [
24146                                 {
24147                                     tag: 'span',
24148                                     cls: 'minutes-down fa fas fa-chevron-down'
24149                                 }
24150                             ]
24151                         }
24152                     ]
24153                 },
24154                 {
24155                     tag: 'td',
24156                     cls: 'separator'
24157                 }
24158             ]
24159         });
24160         
24161     },
24162     
24163     update: function()
24164     {
24165         
24166         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24167         
24168         this.fill();
24169     },
24170     
24171     fill: function() 
24172     {
24173         var hours = this.time.getHours();
24174         var minutes = this.time.getMinutes();
24175         var period = 'AM';
24176         
24177         if(hours > 11){
24178             period = 'PM';
24179         }
24180         
24181         if(hours == 0){
24182             hours = 12;
24183         }
24184         
24185         
24186         if(hours > 12){
24187             hours = hours - 12;
24188         }
24189         
24190         if(hours < 10){
24191             hours = '0' + hours;
24192         }
24193         
24194         if(minutes < 10){
24195             minutes = '0' + minutes;
24196         }
24197         
24198         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24199         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24200         this.pop.select('button', true).first().dom.innerHTML = period;
24201         
24202     },
24203     
24204     place: function()
24205     {   
24206         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24207         
24208         var cls = ['bottom'];
24209         
24210         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24211             cls.pop();
24212             cls.push('top');
24213         }
24214         
24215         cls.push('right');
24216         
24217         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24218             cls.pop();
24219             cls.push('left');
24220         }
24221         //this.picker().setXY(20000,20000);
24222         this.picker().addClass(cls.join('-'));
24223         
24224         var _this = this;
24225         
24226         Roo.each(cls, function(c){
24227             if(c == 'bottom'){
24228                 (function() {
24229                  //  
24230                 }).defer(200);
24231                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24232                 //_this.picker().setTop(_this.inputEl().getHeight());
24233                 return;
24234             }
24235             if(c == 'top'){
24236                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24237                 
24238                 //_this.picker().setTop(0 - _this.picker().getHeight());
24239                 return;
24240             }
24241             /*
24242             if(c == 'left'){
24243                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24244                 return;
24245             }
24246             if(c == 'right'){
24247                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24248                 return;
24249             }
24250             */
24251         });
24252         
24253     },
24254   
24255     onFocus : function()
24256     {
24257         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24258         this.show();
24259     },
24260     
24261     onBlur : function()
24262     {
24263         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24264         this.hide();
24265     },
24266     
24267     show : function()
24268     {
24269         this.picker().show();
24270         this.pop.show();
24271         this.update();
24272         this.place();
24273         
24274         this.fireEvent('show', this, this.date);
24275     },
24276     
24277     hide : function()
24278     {
24279         this.picker().hide();
24280         this.pop.hide();
24281         
24282         this.fireEvent('hide', this, this.date);
24283     },
24284     
24285     setTime : function()
24286     {
24287         this.hide();
24288         this.setValue(this.time.format(this.format));
24289         
24290         this.fireEvent('select', this, this.date);
24291         
24292         
24293     },
24294     
24295     onMousedown: function(e){
24296         e.stopPropagation();
24297         e.preventDefault();
24298     },
24299     
24300     onIncrementHours: function()
24301     {
24302         Roo.log('onIncrementHours');
24303         this.time = this.time.add(Date.HOUR, 1);
24304         this.update();
24305         
24306     },
24307     
24308     onDecrementHours: function()
24309     {
24310         Roo.log('onDecrementHours');
24311         this.time = this.time.add(Date.HOUR, -1);
24312         this.update();
24313     },
24314     
24315     onIncrementMinutes: function()
24316     {
24317         Roo.log('onIncrementMinutes');
24318         this.time = this.time.add(Date.MINUTE, 1);
24319         this.update();
24320     },
24321     
24322     onDecrementMinutes: function()
24323     {
24324         Roo.log('onDecrementMinutes');
24325         this.time = this.time.add(Date.MINUTE, -1);
24326         this.update();
24327     },
24328     
24329     onTogglePeriod: function()
24330     {
24331         Roo.log('onTogglePeriod');
24332         this.time = this.time.add(Date.HOUR, 12);
24333         this.update();
24334     }
24335     
24336    
24337 });
24338  
24339
24340 Roo.apply(Roo.bootstrap.form.TimeField,  {
24341   
24342     template : {
24343         tag: 'div',
24344         cls: 'datepicker dropdown-menu',
24345         cn: [
24346             {
24347                 tag: 'div',
24348                 cls: 'datepicker-time',
24349                 cn: [
24350                 {
24351                     tag: 'table',
24352                     cls: 'table-condensed',
24353                     cn:[
24354                         {
24355                             tag: 'tbody',
24356                             cn: [
24357                                 {
24358                                     tag: 'tr',
24359                                     cn: [
24360                                     {
24361                                         tag: 'td',
24362                                         colspan: '7'
24363                                     }
24364                                     ]
24365                                 }
24366                             ]
24367                         },
24368                         {
24369                             tag: 'tfoot',
24370                             cn: [
24371                                 {
24372                                     tag: 'tr',
24373                                     cn: [
24374                                     {
24375                                         tag: 'th',
24376                                         colspan: '7',
24377                                         cls: '',
24378                                         cn: [
24379                                             {
24380                                                 tag: 'button',
24381                                                 cls: 'btn btn-info ok',
24382                                                 html: 'OK'
24383                                             }
24384                                         ]
24385                                     }
24386                     
24387                                     ]
24388                                 }
24389                             ]
24390                         }
24391                     ]
24392                 }
24393                 ]
24394             }
24395         ]
24396     }
24397 });
24398
24399  
24400
24401  /*
24402  * - LGPL
24403  *
24404  * MonthField
24405  * 
24406  */
24407
24408 /**
24409  * @class Roo.bootstrap.form.MonthField
24410  * @extends Roo.bootstrap.form.Input
24411  * Bootstrap MonthField class
24412  * 
24413  * @cfg {String} language default en
24414  * 
24415  * @constructor
24416  * Create a new MonthField
24417  * @param {Object} config The config object
24418  */
24419
24420 Roo.bootstrap.form.MonthField = function(config){
24421     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24422     
24423     this.addEvents({
24424         /**
24425          * @event show
24426          * Fires when this field show.
24427          * @param {Roo.bootstrap.form.MonthField} this
24428          * @param {Mixed} date The date value
24429          */
24430         show : true,
24431         /**
24432          * @event show
24433          * Fires when this field hide.
24434          * @param {Roo.bootstrap.form.MonthField} this
24435          * @param {Mixed} date The date value
24436          */
24437         hide : true,
24438         /**
24439          * @event select
24440          * Fires when select a date.
24441          * @param {Roo.bootstrap.form.MonthField} this
24442          * @param {String} oldvalue The old value
24443          * @param {String} newvalue The new value
24444          */
24445         select : true
24446     });
24447 };
24448
24449 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24450     
24451     onRender: function(ct, position)
24452     {
24453         
24454         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24455         
24456         this.language = this.language || 'en';
24457         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24458         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24459         
24460         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24461         this.isInline = false;
24462         this.isInput = true;
24463         this.component = this.el.select('.add-on', true).first() || false;
24464         this.component = (this.component && this.component.length === 0) ? false : this.component;
24465         this.hasInput = this.component && this.inputEL().length;
24466         
24467         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24468         
24469         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24470         
24471         this.picker().on('mousedown', this.onMousedown, this);
24472         this.picker().on('click', this.onClick, this);
24473         
24474         this.picker().addClass('datepicker-dropdown');
24475         
24476         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24477             v.setStyle('width', '189px');
24478         });
24479         
24480         this.fillMonths();
24481         
24482         this.update();
24483         
24484         if(this.isInline) {
24485             this.show();
24486         }
24487         
24488     },
24489     
24490     setValue: function(v, suppressEvent)
24491     {   
24492         var o = this.getValue();
24493         
24494         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24495         
24496         this.update();
24497
24498         if(suppressEvent !== true){
24499             this.fireEvent('select', this, o, v);
24500         }
24501         
24502     },
24503     
24504     getValue: function()
24505     {
24506         return this.value;
24507     },
24508     
24509     onClick: function(e) 
24510     {
24511         e.stopPropagation();
24512         e.preventDefault();
24513         
24514         var target = e.getTarget();
24515         
24516         if(target.nodeName.toLowerCase() === 'i'){
24517             target = Roo.get(target).dom.parentNode;
24518         }
24519         
24520         var nodeName = target.nodeName;
24521         var className = target.className;
24522         var html = target.innerHTML;
24523         
24524         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24525             return;
24526         }
24527         
24528         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24529         
24530         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24531         
24532         this.hide();
24533                         
24534     },
24535     
24536     picker : function()
24537     {
24538         return this.pickerEl;
24539     },
24540     
24541     fillMonths: function()
24542     {    
24543         var i = 0;
24544         var months = this.picker().select('>.datepicker-months td', true).first();
24545         
24546         months.dom.innerHTML = '';
24547         
24548         while (i < 12) {
24549             var month = {
24550                 tag: 'span',
24551                 cls: 'month',
24552                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24553             };
24554             
24555             months.createChild(month);
24556         }
24557         
24558     },
24559     
24560     update: function()
24561     {
24562         var _this = this;
24563         
24564         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24565             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24566         }
24567         
24568         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24569             e.removeClass('active');
24570             
24571             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24572                 e.addClass('active');
24573             }
24574         })
24575     },
24576     
24577     place: function()
24578     {
24579         if(this.isInline) {
24580             return;
24581         }
24582         
24583         this.picker().removeClass(['bottom', 'top']);
24584         
24585         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24586             /*
24587              * place to the top of element!
24588              *
24589              */
24590             
24591             this.picker().addClass('top');
24592             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24593             
24594             return;
24595         }
24596         
24597         this.picker().addClass('bottom');
24598         
24599         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24600     },
24601     
24602     onFocus : function()
24603     {
24604         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24605         this.show();
24606     },
24607     
24608     onBlur : function()
24609     {
24610         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24611         
24612         var d = this.inputEl().getValue();
24613         
24614         this.setValue(d);
24615                 
24616         this.hide();
24617     },
24618     
24619     show : function()
24620     {
24621         this.picker().show();
24622         this.picker().select('>.datepicker-months', true).first().show();
24623         this.update();
24624         this.place();
24625         
24626         this.fireEvent('show', this, this.date);
24627     },
24628     
24629     hide : function()
24630     {
24631         if(this.isInline) {
24632             return;
24633         }
24634         this.picker().hide();
24635         this.fireEvent('hide', this, this.date);
24636         
24637     },
24638     
24639     onMousedown: function(e)
24640     {
24641         e.stopPropagation();
24642         e.preventDefault();
24643     },
24644     
24645     keyup: function(e)
24646     {
24647         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24648         this.update();
24649     },
24650
24651     fireKey: function(e)
24652     {
24653         if (!this.picker().isVisible()){
24654             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24655                 this.show();
24656             }
24657             return;
24658         }
24659         
24660         var dir;
24661         
24662         switch(e.keyCode){
24663             case 27: // escape
24664                 this.hide();
24665                 e.preventDefault();
24666                 break;
24667             case 37: // left
24668             case 39: // right
24669                 dir = e.keyCode == 37 ? -1 : 1;
24670                 
24671                 this.vIndex = this.vIndex + dir;
24672                 
24673                 if(this.vIndex < 0){
24674                     this.vIndex = 0;
24675                 }
24676                 
24677                 if(this.vIndex > 11){
24678                     this.vIndex = 11;
24679                 }
24680                 
24681                 if(isNaN(this.vIndex)){
24682                     this.vIndex = 0;
24683                 }
24684                 
24685                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24686                 
24687                 break;
24688             case 38: // up
24689             case 40: // down
24690                 
24691                 dir = e.keyCode == 38 ? -1 : 1;
24692                 
24693                 this.vIndex = this.vIndex + dir * 4;
24694                 
24695                 if(this.vIndex < 0){
24696                     this.vIndex = 0;
24697                 }
24698                 
24699                 if(this.vIndex > 11){
24700                     this.vIndex = 11;
24701                 }
24702                 
24703                 if(isNaN(this.vIndex)){
24704                     this.vIndex = 0;
24705                 }
24706                 
24707                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24708                 break;
24709                 
24710             case 13: // enter
24711                 
24712                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24713                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24714                 }
24715                 
24716                 this.hide();
24717                 e.preventDefault();
24718                 break;
24719             case 9: // tab
24720                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24721                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24722                 }
24723                 this.hide();
24724                 break;
24725             case 16: // shift
24726             case 17: // ctrl
24727             case 18: // alt
24728                 break;
24729             default :
24730                 this.hide();
24731                 
24732         }
24733     },
24734     
24735     remove: function() 
24736     {
24737         this.picker().remove();
24738     }
24739    
24740 });
24741
24742 Roo.apply(Roo.bootstrap.form.MonthField,  {
24743     
24744     content : {
24745         tag: 'tbody',
24746         cn: [
24747         {
24748             tag: 'tr',
24749             cn: [
24750             {
24751                 tag: 'td',
24752                 colspan: '7'
24753             }
24754             ]
24755         }
24756         ]
24757     },
24758     
24759     dates:{
24760         en: {
24761             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24762             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24763         }
24764     }
24765 });
24766
24767 Roo.apply(Roo.bootstrap.form.MonthField,  {
24768   
24769     template : {
24770         tag: 'div',
24771         cls: 'datepicker dropdown-menu roo-dynamic',
24772         cn: [
24773             {
24774                 tag: 'div',
24775                 cls: 'datepicker-months',
24776                 cn: [
24777                 {
24778                     tag: 'table',
24779                     cls: 'table-condensed',
24780                     cn:[
24781                         Roo.bootstrap.form.DateField.content
24782                     ]
24783                 }
24784                 ]
24785             }
24786         ]
24787     }
24788 });
24789
24790  
24791
24792  
24793  /*
24794  * - LGPL
24795  *
24796  * CheckBox
24797  * 
24798  */
24799
24800 /**
24801  * @class Roo.bootstrap.form.CheckBox
24802  * @extends Roo.bootstrap.form.Input
24803  * Bootstrap CheckBox class
24804  * 
24805  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24806  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24807  * @cfg {String} boxLabel The text that appears beside the checkbox
24808  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24809  * @cfg {Boolean} checked initnal the element
24810  * @cfg {Boolean} inline inline the element (default false)
24811  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24812  * @cfg {String} tooltip label tooltip
24813  * 
24814  * @constructor
24815  * Create a new CheckBox
24816  * @param {Object} config The config object
24817  */
24818
24819 Roo.bootstrap.form.CheckBox = function(config){
24820     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24821    
24822     this.addEvents({
24823         /**
24824         * @event check
24825         * Fires when the element is checked or unchecked.
24826         * @param {Roo.bootstrap.form.CheckBox} this This input
24827         * @param {Boolean} checked The new checked value
24828         */
24829        check : true,
24830        /**
24831         * @event click
24832         * Fires when the element is click.
24833         * @param {Roo.bootstrap.form.CheckBox} this This input
24834         */
24835        click : true
24836     });
24837     
24838 };
24839
24840 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24841   
24842     inputType: 'checkbox',
24843     inputValue: 1,
24844     valueOff: 0,
24845     boxLabel: false,
24846     checked: false,
24847     weight : false,
24848     inline: false,
24849     tooltip : '',
24850     
24851     // checkbox success does not make any sense really.. 
24852     invalidClass : "",
24853     validClass : "",
24854     
24855     
24856     getAutoCreate : function()
24857     {
24858         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24859         
24860         var id = Roo.id();
24861         
24862         var cfg = {};
24863         
24864         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24865         
24866         if(this.inline){
24867             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24868         }
24869         
24870         var input =  {
24871             tag: 'input',
24872             id : id,
24873             type : this.inputType,
24874             value : this.inputValue,
24875             cls : 'roo-' + this.inputType, //'form-box',
24876             placeholder : this.placeholder || ''
24877             
24878         };
24879         
24880         if(this.inputType != 'radio'){
24881             var hidden =  {
24882                 tag: 'input',
24883                 type : 'hidden',
24884                 cls : 'roo-hidden-value',
24885                 value : this.checked ? this.inputValue : this.valueOff
24886             };
24887         }
24888         
24889             
24890         if (this.weight) { // Validity check?
24891             cfg.cls += " " + this.inputType + "-" + this.weight;
24892         }
24893         
24894         if (this.disabled) {
24895             input.disabled=true;
24896         }
24897         
24898         if(this.checked){
24899             input.checked = this.checked;
24900         }
24901         
24902         if (this.name) {
24903             
24904             input.name = this.name;
24905             
24906             if(this.inputType != 'radio'){
24907                 hidden.name = this.name;
24908                 input.name = '_hidden_' + this.name;
24909             }
24910         }
24911         
24912         if (this.size) {
24913             input.cls += ' input-' + this.size;
24914         }
24915         
24916         var settings=this;
24917         
24918         ['xs','sm','md','lg'].map(function(size){
24919             if (settings[size]) {
24920                 cfg.cls += ' col-' + size + '-' + settings[size];
24921             }
24922         });
24923         
24924         var inputblock = input;
24925          
24926         if (this.before || this.after) {
24927             
24928             inputblock = {
24929                 cls : 'input-group',
24930                 cn :  [] 
24931             };
24932             
24933             if (this.before) {
24934                 inputblock.cn.push({
24935                     tag :'span',
24936                     cls : 'input-group-addon',
24937                     html : this.before
24938                 });
24939             }
24940             
24941             inputblock.cn.push(input);
24942             
24943             if(this.inputType != 'radio'){
24944                 inputblock.cn.push(hidden);
24945             }
24946             
24947             if (this.after) {
24948                 inputblock.cn.push({
24949                     tag :'span',
24950                     cls : 'input-group-addon',
24951                     html : this.after
24952                 });
24953             }
24954             
24955         }
24956         var boxLabelCfg = false;
24957         
24958         if(this.boxLabel){
24959            
24960             boxLabelCfg = {
24961                 tag: 'label',
24962                 //'for': id, // box label is handled by onclick - so no for...
24963                 cls: 'box-label',
24964                 html: this.boxLabel
24965             };
24966             if(this.tooltip){
24967                 boxLabelCfg.tooltip = this.tooltip;
24968             }
24969              
24970         }
24971         
24972         
24973         if (align ==='left' && this.fieldLabel.length) {
24974 //                Roo.log("left and has label");
24975             cfg.cn = [
24976                 {
24977                     tag: 'label',
24978                     'for' :  id,
24979                     cls : 'control-label',
24980                     html : this.fieldLabel
24981                 },
24982                 {
24983                     cls : "", 
24984                     cn: [
24985                         inputblock
24986                     ]
24987                 }
24988             ];
24989             
24990             if (boxLabelCfg) {
24991                 cfg.cn[1].cn.push(boxLabelCfg);
24992             }
24993             
24994             if(this.labelWidth > 12){
24995                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24996             }
24997             
24998             if(this.labelWidth < 13 && this.labelmd == 0){
24999                 this.labelmd = this.labelWidth;
25000             }
25001             
25002             if(this.labellg > 0){
25003                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25004                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25005             }
25006             
25007             if(this.labelmd > 0){
25008                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25009                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25010             }
25011             
25012             if(this.labelsm > 0){
25013                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25014                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25015             }
25016             
25017             if(this.labelxs > 0){
25018                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25019                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25020             }
25021             
25022         } else if ( this.fieldLabel.length) {
25023 //                Roo.log(" label");
25024                 cfg.cn = [
25025                    
25026                     {
25027                         tag: this.boxLabel ? 'span' : 'label',
25028                         'for': id,
25029                         cls: 'control-label box-input-label',
25030                         //cls : 'input-group-addon',
25031                         html : this.fieldLabel
25032                     },
25033                     
25034                     inputblock
25035                     
25036                 ];
25037                 if (boxLabelCfg) {
25038                     cfg.cn.push(boxLabelCfg);
25039                 }
25040
25041         } else {
25042             
25043 //                Roo.log(" no label && no align");
25044                 cfg.cn = [  inputblock ] ;
25045                 if (boxLabelCfg) {
25046                     cfg.cn.push(boxLabelCfg);
25047                 }
25048
25049                 
25050         }
25051         
25052        
25053         
25054         if(this.inputType != 'radio'){
25055             cfg.cn.push(hidden);
25056         }
25057         
25058         return cfg;
25059         
25060     },
25061     
25062     /**
25063      * return the real input element.
25064      */
25065     inputEl: function ()
25066     {
25067         return this.el.select('input.roo-' + this.inputType,true).first();
25068     },
25069     hiddenEl: function ()
25070     {
25071         return this.el.select('input.roo-hidden-value',true).first();
25072     },
25073     
25074     labelEl: function()
25075     {
25076         return this.el.select('label.control-label',true).first();
25077     },
25078     /* depricated... */
25079     
25080     label: function()
25081     {
25082         return this.labelEl();
25083     },
25084     
25085     boxLabelEl: function()
25086     {
25087         return this.el.select('label.box-label',true).first();
25088     },
25089     
25090     initEvents : function()
25091     {
25092 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25093         
25094         this.inputEl().on('click', this.onClick,  this);
25095         
25096         if (this.boxLabel) { 
25097             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25098         }
25099         
25100         this.startValue = this.getValue();
25101         
25102         if(this.groupId){
25103             Roo.bootstrap.form.CheckBox.register(this);
25104         }
25105     },
25106     
25107     onClick : function(e)
25108     {   
25109         if(this.fireEvent('click', this, e) !== false){
25110             this.setChecked(!this.checked);
25111         }
25112         
25113     },
25114     
25115     setChecked : function(state,suppressEvent)
25116     {
25117         this.startValue = this.getValue();
25118
25119         if(this.inputType == 'radio'){
25120             
25121             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25122                 e.dom.checked = false;
25123             });
25124             
25125             this.inputEl().dom.checked = true;
25126             
25127             this.inputEl().dom.value = this.inputValue;
25128             
25129             if(suppressEvent !== true){
25130                 this.fireEvent('check', this, true);
25131             }
25132             
25133             this.validate();
25134             
25135             return;
25136         }
25137         
25138         this.checked = state;
25139         
25140         this.inputEl().dom.checked = state;
25141         
25142         
25143         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25144         
25145         if(suppressEvent !== true){
25146             this.fireEvent('check', this, state);
25147         }
25148         
25149         this.validate();
25150     },
25151     
25152     getValue : function()
25153     {
25154         if(this.inputType == 'radio'){
25155             return this.getGroupValue();
25156         }
25157         
25158         return this.hiddenEl().dom.value;
25159         
25160     },
25161     
25162     getGroupValue : function()
25163     {
25164         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25165             return '';
25166         }
25167         
25168         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25169     },
25170     
25171     setValue : function(v,suppressEvent)
25172     {
25173         if(this.inputType == 'radio'){
25174             this.setGroupValue(v, suppressEvent);
25175             return;
25176         }
25177         
25178         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25179         
25180         this.validate();
25181     },
25182     
25183     setGroupValue : function(v, suppressEvent)
25184     {
25185         this.startValue = this.getValue();
25186         
25187         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25188             e.dom.checked = false;
25189             
25190             if(e.dom.value == v){
25191                 e.dom.checked = true;
25192             }
25193         });
25194         
25195         if(suppressEvent !== true){
25196             this.fireEvent('check', this, true);
25197         }
25198
25199         this.validate();
25200         
25201         return;
25202     },
25203     
25204     validate : function()
25205     {
25206         if(this.getVisibilityEl().hasClass('hidden')){
25207             return true;
25208         }
25209         
25210         if(
25211                 this.disabled || 
25212                 (this.inputType == 'radio' && this.validateRadio()) ||
25213                 (this.inputType == 'checkbox' && this.validateCheckbox())
25214         ){
25215             this.markValid();
25216             return true;
25217         }
25218         
25219         this.markInvalid();
25220         return false;
25221     },
25222     
25223     validateRadio : function()
25224     {
25225         if(this.getVisibilityEl().hasClass('hidden')){
25226             return true;
25227         }
25228         
25229         if(this.allowBlank){
25230             return true;
25231         }
25232         
25233         var valid = false;
25234         
25235         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25236             if(!e.dom.checked){
25237                 return;
25238             }
25239             
25240             valid = true;
25241             
25242             return false;
25243         });
25244         
25245         return valid;
25246     },
25247     
25248     validateCheckbox : function()
25249     {
25250         if(!this.groupId){
25251             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25252             //return (this.getValue() == this.inputValue) ? true : false;
25253         }
25254         
25255         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25256         
25257         if(!group){
25258             return false;
25259         }
25260         
25261         var r = false;
25262         
25263         for(var i in group){
25264             if(group[i].el.isVisible(true)){
25265                 r = false;
25266                 break;
25267             }
25268             
25269             r = true;
25270         }
25271         
25272         for(var i in group){
25273             if(r){
25274                 break;
25275             }
25276             
25277             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25278         }
25279         
25280         return r;
25281     },
25282     
25283     /**
25284      * Mark this field as valid
25285      */
25286     markValid : function()
25287     {
25288         var _this = this;
25289         
25290         this.fireEvent('valid', this);
25291         
25292         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25293         
25294         if(this.groupId){
25295             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25296         }
25297         
25298         if(label){
25299             label.markValid();
25300         }
25301
25302         if(this.inputType == 'radio'){
25303             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25304                 var fg = e.findParent('.form-group', false, true);
25305                 if (Roo.bootstrap.version == 3) {
25306                     fg.removeClass([_this.invalidClass, _this.validClass]);
25307                     fg.addClass(_this.validClass);
25308                 } else {
25309                     fg.removeClass(['is-valid', 'is-invalid']);
25310                     fg.addClass('is-valid');
25311                 }
25312             });
25313             
25314             return;
25315         }
25316
25317         if(!this.groupId){
25318             var fg = this.el.findParent('.form-group', false, true);
25319             if (Roo.bootstrap.version == 3) {
25320                 fg.removeClass([this.invalidClass, this.validClass]);
25321                 fg.addClass(this.validClass);
25322             } else {
25323                 fg.removeClass(['is-valid', 'is-invalid']);
25324                 fg.addClass('is-valid');
25325             }
25326             return;
25327         }
25328         
25329         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25330         
25331         if(!group){
25332             return;
25333         }
25334         
25335         for(var i in group){
25336             var fg = group[i].el.findParent('.form-group', false, true);
25337             if (Roo.bootstrap.version == 3) {
25338                 fg.removeClass([this.invalidClass, this.validClass]);
25339                 fg.addClass(this.validClass);
25340             } else {
25341                 fg.removeClass(['is-valid', 'is-invalid']);
25342                 fg.addClass('is-valid');
25343             }
25344         }
25345     },
25346     
25347      /**
25348      * Mark this field as invalid
25349      * @param {String} msg The validation message
25350      */
25351     markInvalid : function(msg)
25352     {
25353         if(this.allowBlank){
25354             return;
25355         }
25356         
25357         var _this = this;
25358         
25359         this.fireEvent('invalid', this, msg);
25360         
25361         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25362         
25363         if(this.groupId){
25364             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25365         }
25366         
25367         if(label){
25368             label.markInvalid();
25369         }
25370             
25371         if(this.inputType == 'radio'){
25372             
25373             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25374                 var fg = e.findParent('.form-group', false, true);
25375                 if (Roo.bootstrap.version == 3) {
25376                     fg.removeClass([_this.invalidClass, _this.validClass]);
25377                     fg.addClass(_this.invalidClass);
25378                 } else {
25379                     fg.removeClass(['is-invalid', 'is-valid']);
25380                     fg.addClass('is-invalid');
25381                 }
25382             });
25383             
25384             return;
25385         }
25386         
25387         if(!this.groupId){
25388             var fg = this.el.findParent('.form-group', false, true);
25389             if (Roo.bootstrap.version == 3) {
25390                 fg.removeClass([_this.invalidClass, _this.validClass]);
25391                 fg.addClass(_this.invalidClass);
25392             } else {
25393                 fg.removeClass(['is-invalid', 'is-valid']);
25394                 fg.addClass('is-invalid');
25395             }
25396             return;
25397         }
25398         
25399         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25400         
25401         if(!group){
25402             return;
25403         }
25404         
25405         for(var i in group){
25406             var fg = group[i].el.findParent('.form-group', false, true);
25407             if (Roo.bootstrap.version == 3) {
25408                 fg.removeClass([_this.invalidClass, _this.validClass]);
25409                 fg.addClass(_this.invalidClass);
25410             } else {
25411                 fg.removeClass(['is-invalid', 'is-valid']);
25412                 fg.addClass('is-invalid');
25413             }
25414         }
25415         
25416     },
25417     
25418     clearInvalid : function()
25419     {
25420         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25421         
25422         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25423         
25424         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25425         
25426         if (label && label.iconEl) {
25427             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25428             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25429         }
25430     },
25431     
25432     disable : function()
25433     {
25434         if(this.inputType != 'radio'){
25435             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25436             return;
25437         }
25438         
25439         var _this = this;
25440         
25441         if(this.rendered){
25442             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25443                 _this.getActionEl().addClass(this.disabledClass);
25444                 e.dom.disabled = true;
25445             });
25446         }
25447         
25448         this.disabled = true;
25449         this.fireEvent("disable", this);
25450         return this;
25451     },
25452
25453     enable : function()
25454     {
25455         if(this.inputType != 'radio'){
25456             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25457             return;
25458         }
25459         
25460         var _this = this;
25461         
25462         if(this.rendered){
25463             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25464                 _this.getActionEl().removeClass(this.disabledClass);
25465                 e.dom.disabled = false;
25466             });
25467         }
25468         
25469         this.disabled = false;
25470         this.fireEvent("enable", this);
25471         return this;
25472     },
25473     
25474     setBoxLabel : function(v)
25475     {
25476         this.boxLabel = v;
25477         
25478         if(this.rendered){
25479             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25480         }
25481     }
25482
25483 });
25484
25485 Roo.apply(Roo.bootstrap.form.CheckBox, {
25486     
25487     groups: {},
25488     
25489      /**
25490     * register a CheckBox Group
25491     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25492     */
25493     register : function(checkbox)
25494     {
25495         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25496             this.groups[checkbox.groupId] = {};
25497         }
25498         
25499         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25500             return;
25501         }
25502         
25503         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25504         
25505     },
25506     /**
25507     * fetch a CheckBox Group based on the group ID
25508     * @param {string} the group ID
25509     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25510     */
25511     get: function(groupId) {
25512         if (typeof(this.groups[groupId]) == 'undefined') {
25513             return false;
25514         }
25515         
25516         return this.groups[groupId] ;
25517     }
25518     
25519     
25520 });
25521 /*
25522  * - LGPL
25523  *
25524  * RadioItem
25525  * 
25526  */
25527
25528 /**
25529  * @class Roo.bootstrap.form.Radio
25530  * @extends Roo.bootstrap.Component
25531  * Bootstrap Radio class
25532  * @cfg {String} boxLabel - the label associated
25533  * @cfg {String} value - the value of radio
25534  * 
25535  * @constructor
25536  * Create a new Radio
25537  * @param {Object} config The config object
25538  */
25539 Roo.bootstrap.form.Radio = function(config){
25540     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25541     
25542 };
25543
25544 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25545     
25546     boxLabel : '',
25547     
25548     value : '',
25549     
25550     getAutoCreate : function()
25551     {
25552         var cfg = {
25553             tag : 'div',
25554             cls : 'form-group radio',
25555             cn : [
25556                 {
25557                     tag : 'label',
25558                     cls : 'box-label',
25559                     html : this.boxLabel
25560                 }
25561             ]
25562         };
25563         
25564         return cfg;
25565     },
25566     
25567     initEvents : function() 
25568     {
25569         this.parent().register(this);
25570         
25571         this.el.on('click', this.onClick, this);
25572         
25573     },
25574     
25575     onClick : function(e)
25576     {
25577         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25578             this.setChecked(true);
25579         }
25580     },
25581     
25582     setChecked : function(state, suppressEvent)
25583     {
25584         this.parent().setValue(this.value, suppressEvent);
25585         
25586     },
25587     
25588     setBoxLabel : function(v)
25589     {
25590         this.boxLabel = v;
25591         
25592         if(this.rendered){
25593             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25594         }
25595     }
25596     
25597 });
25598  
25599
25600  /*
25601  * - LGPL
25602  *
25603  * Input
25604  * 
25605  */
25606
25607 /**
25608  * @class Roo.bootstrap.form.SecurePass
25609  * @extends Roo.bootstrap.form.Input
25610  * Bootstrap SecurePass class
25611  *
25612  * 
25613  * @constructor
25614  * Create a new SecurePass
25615  * @param {Object} config The config object
25616  */
25617  
25618 Roo.bootstrap.form.SecurePass = function (config) {
25619     // these go here, so the translation tool can replace them..
25620     this.errors = {
25621         PwdEmpty: "Please type a password, and then retype it to confirm.",
25622         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25623         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25624         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25625         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25626         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25627         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25628         TooWeak: "Your password is Too Weak."
25629     },
25630     this.meterLabel = "Password strength:";
25631     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25632     this.meterClass = [
25633         "roo-password-meter-tooweak", 
25634         "roo-password-meter-weak", 
25635         "roo-password-meter-medium", 
25636         "roo-password-meter-strong", 
25637         "roo-password-meter-grey"
25638     ];
25639     
25640     this.errors = {};
25641     
25642     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25643 }
25644
25645 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25646     /**
25647      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25648      * {
25649      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25650      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25651      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25652      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25653      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25654      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25655      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25656      * })
25657      */
25658     // private
25659     
25660     meterWidth: 300,
25661     errorMsg :'',    
25662     errors: false,
25663     imageRoot: '/',
25664     /**
25665      * @cfg {String/Object} Label for the strength meter (defaults to
25666      * 'Password strength:')
25667      */
25668     // private
25669     meterLabel: '',
25670     /**
25671      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25672      * ['Weak', 'Medium', 'Strong'])
25673      */
25674     // private    
25675     pwdStrengths: false,    
25676     // private
25677     strength: 0,
25678     // private
25679     _lastPwd: null,
25680     // private
25681     kCapitalLetter: 0,
25682     kSmallLetter: 1,
25683     kDigit: 2,
25684     kPunctuation: 3,
25685     
25686     insecure: false,
25687     // private
25688     initEvents: function ()
25689     {
25690         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25691
25692         if (this.el.is('input[type=password]') && Roo.isSafari) {
25693             this.el.on('keydown', this.SafariOnKeyDown, this);
25694         }
25695
25696         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25697     },
25698     // private
25699     onRender: function (ct, position)
25700     {
25701         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25702         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25703         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25704
25705         this.trigger.createChild({
25706                    cn: [
25707                     {
25708                     //id: 'PwdMeter',
25709                     tag: 'div',
25710                     cls: 'roo-password-meter-grey col-xs-12',
25711                     style: {
25712                         //width: 0,
25713                         //width: this.meterWidth + 'px'                                                
25714                         }
25715                     },
25716                     {                            
25717                          cls: 'roo-password-meter-text'                          
25718                     }
25719                 ]            
25720         });
25721
25722          
25723         if (this.hideTrigger) {
25724             this.trigger.setDisplayed(false);
25725         }
25726         this.setSize(this.width || '', this.height || '');
25727     },
25728     // private
25729     onDestroy: function ()
25730     {
25731         if (this.trigger) {
25732             this.trigger.removeAllListeners();
25733             this.trigger.remove();
25734         }
25735         if (this.wrap) {
25736             this.wrap.remove();
25737         }
25738         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25739     },
25740     // private
25741     checkStrength: function ()
25742     {
25743         var pwd = this.inputEl().getValue();
25744         if (pwd == this._lastPwd) {
25745             return;
25746         }
25747
25748         var strength;
25749         if (this.ClientSideStrongPassword(pwd)) {
25750             strength = 3;
25751         } else if (this.ClientSideMediumPassword(pwd)) {
25752             strength = 2;
25753         } else if (this.ClientSideWeakPassword(pwd)) {
25754             strength = 1;
25755         } else {
25756             strength = 0;
25757         }
25758         
25759         Roo.log('strength1: ' + strength);
25760         
25761         //var pm = this.trigger.child('div/div/div').dom;
25762         var pm = this.trigger.child('div/div');
25763         pm.removeClass(this.meterClass);
25764         pm.addClass(this.meterClass[strength]);
25765                 
25766         
25767         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25768                 
25769         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25770         
25771         this._lastPwd = pwd;
25772     },
25773     reset: function ()
25774     {
25775         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25776         
25777         this._lastPwd = '';
25778         
25779         var pm = this.trigger.child('div/div');
25780         pm.removeClass(this.meterClass);
25781         pm.addClass('roo-password-meter-grey');        
25782         
25783         
25784         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25785         
25786         pt.innerHTML = '';
25787         this.inputEl().dom.type='password';
25788     },
25789     // private
25790     validateValue: function (value)
25791     {
25792         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25793             return false;
25794         }
25795         if (value.length == 0) {
25796             if (this.allowBlank) {
25797                 this.clearInvalid();
25798                 return true;
25799             }
25800
25801             this.markInvalid(this.errors.PwdEmpty);
25802             this.errorMsg = this.errors.PwdEmpty;
25803             return false;
25804         }
25805         
25806         if(this.insecure){
25807             return true;
25808         }
25809         
25810         if (!value.match(/[\x21-\x7e]+/)) {
25811             this.markInvalid(this.errors.PwdBadChar);
25812             this.errorMsg = this.errors.PwdBadChar;
25813             return false;
25814         }
25815         if (value.length < 6) {
25816             this.markInvalid(this.errors.PwdShort);
25817             this.errorMsg = this.errors.PwdShort;
25818             return false;
25819         }
25820         if (value.length > 16) {
25821             this.markInvalid(this.errors.PwdLong);
25822             this.errorMsg = this.errors.PwdLong;
25823             return false;
25824         }
25825         var strength;
25826         if (this.ClientSideStrongPassword(value)) {
25827             strength = 3;
25828         } else if (this.ClientSideMediumPassword(value)) {
25829             strength = 2;
25830         } else if (this.ClientSideWeakPassword(value)) {
25831             strength = 1;
25832         } else {
25833             strength = 0;
25834         }
25835
25836         
25837         if (strength < 2) {
25838             //this.markInvalid(this.errors.TooWeak);
25839             this.errorMsg = this.errors.TooWeak;
25840             //return false;
25841         }
25842         
25843         
25844         console.log('strength2: ' + strength);
25845         
25846         //var pm = this.trigger.child('div/div/div').dom;
25847         
25848         var pm = this.trigger.child('div/div');
25849         pm.removeClass(this.meterClass);
25850         pm.addClass(this.meterClass[strength]);
25851                 
25852         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25853                 
25854         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25855         
25856         this.errorMsg = ''; 
25857         return true;
25858     },
25859     // private
25860     CharacterSetChecks: function (type)
25861     {
25862         this.type = type;
25863         this.fResult = false;
25864     },
25865     // private
25866     isctype: function (character, type)
25867     {
25868         switch (type) {  
25869             case this.kCapitalLetter:
25870                 if (character >= 'A' && character <= 'Z') {
25871                     return true;
25872                 }
25873                 break;
25874             
25875             case this.kSmallLetter:
25876                 if (character >= 'a' && character <= 'z') {
25877                     return true;
25878                 }
25879                 break;
25880             
25881             case this.kDigit:
25882                 if (character >= '0' && character <= '9') {
25883                     return true;
25884                 }
25885                 break;
25886             
25887             case this.kPunctuation:
25888                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25889                     return true;
25890                 }
25891                 break;
25892             
25893             default:
25894                 return false;
25895         }
25896
25897     },
25898     // private
25899     IsLongEnough: function (pwd, size)
25900     {
25901         return !(pwd == null || isNaN(size) || pwd.length < size);
25902     },
25903     // private
25904     SpansEnoughCharacterSets: function (word, nb)
25905     {
25906         if (!this.IsLongEnough(word, nb))
25907         {
25908             return false;
25909         }
25910
25911         var characterSetChecks = new Array(
25912             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25913             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25914         );
25915         
25916         for (var index = 0; index < word.length; ++index) {
25917             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25918                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25919                     characterSetChecks[nCharSet].fResult = true;
25920                     break;
25921                 }
25922             }
25923         }
25924
25925         var nCharSets = 0;
25926         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25927             if (characterSetChecks[nCharSet].fResult) {
25928                 ++nCharSets;
25929             }
25930         }
25931
25932         if (nCharSets < nb) {
25933             return false;
25934         }
25935         return true;
25936     },
25937     // private
25938     ClientSideStrongPassword: function (pwd)
25939     {
25940         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25941     },
25942     // private
25943     ClientSideMediumPassword: function (pwd)
25944     {
25945         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25946     },
25947     // private
25948     ClientSideWeakPassword: function (pwd)
25949     {
25950         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25951     }
25952           
25953 })//<script type="text/javascript">
25954
25955 /*
25956  * Based  Ext JS Library 1.1.1
25957  * Copyright(c) 2006-2007, Ext JS, LLC.
25958  * LGPL
25959  *
25960  */
25961  
25962 /**
25963  * @class Roo.HtmlEditorCore
25964  * @extends Roo.Component
25965  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25966  *
25967  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25968  */
25969
25970 Roo.HtmlEditorCore = function(config){
25971     
25972     
25973     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25974     
25975     
25976     this.addEvents({
25977         /**
25978          * @event initialize
25979          * Fires when the editor is fully initialized (including the iframe)
25980          * @param {Roo.HtmlEditorCore} this
25981          */
25982         initialize: true,
25983         /**
25984          * @event activate
25985          * Fires when the editor is first receives the focus. Any insertion must wait
25986          * until after this event.
25987          * @param {Roo.HtmlEditorCore} this
25988          */
25989         activate: true,
25990          /**
25991          * @event beforesync
25992          * Fires before the textarea is updated with content from the editor iframe. Return false
25993          * to cancel the sync.
25994          * @param {Roo.HtmlEditorCore} this
25995          * @param {String} html
25996          */
25997         beforesync: true,
25998          /**
25999          * @event beforepush
26000          * Fires before the iframe editor is updated with content from the textarea. Return false
26001          * to cancel the push.
26002          * @param {Roo.HtmlEditorCore} this
26003          * @param {String} html
26004          */
26005         beforepush: true,
26006          /**
26007          * @event sync
26008          * Fires when the textarea is updated with content from the editor iframe.
26009          * @param {Roo.HtmlEditorCore} this
26010          * @param {String} html
26011          */
26012         sync: true,
26013          /**
26014          * @event push
26015          * Fires when the iframe editor is updated with content from the textarea.
26016          * @param {Roo.HtmlEditorCore} this
26017          * @param {String} html
26018          */
26019         push: true,
26020         
26021         /**
26022          * @event editorevent
26023          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26024          * @param {Roo.HtmlEditorCore} this
26025          */
26026         editorevent: true
26027         
26028     });
26029     
26030     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26031     
26032     // defaults : white / black...
26033     this.applyBlacklists();
26034     
26035     
26036     
26037 };
26038
26039
26040 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26041
26042
26043      /**
26044      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26045      */
26046     
26047     owner : false,
26048     
26049      /**
26050      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26051      *                        Roo.resizable.
26052      */
26053     resizable : false,
26054      /**
26055      * @cfg {Number} height (in pixels)
26056      */   
26057     height: 300,
26058    /**
26059      * @cfg {Number} width (in pixels)
26060      */   
26061     width: 500,
26062     
26063     /**
26064      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26065      * 
26066      */
26067     stylesheets: false,
26068     
26069     /**
26070      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26071      */
26072     allowComments: false,
26073     // id of frame..
26074     frameId: false,
26075     
26076     // private properties
26077     validationEvent : false,
26078     deferHeight: true,
26079     initialized : false,
26080     activated : false,
26081     sourceEditMode : false,
26082     onFocus : Roo.emptyFn,
26083     iframePad:3,
26084     hideMode:'offsets',
26085     
26086     clearUp: true,
26087     
26088     // blacklist + whitelisted elements..
26089     black: false,
26090     white: false,
26091      
26092     bodyCls : '',
26093
26094     /**
26095      * Protected method that will not generally be called directly. It
26096      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26097      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26098      */
26099     getDocMarkup : function(){
26100         // body styles..
26101         var st = '';
26102         
26103         // inherit styels from page...?? 
26104         if (this.stylesheets === false) {
26105             
26106             Roo.get(document.head).select('style').each(function(node) {
26107                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26108             });
26109             
26110             Roo.get(document.head).select('link').each(function(node) { 
26111                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26112             });
26113             
26114         } else if (!this.stylesheets.length) {
26115                 // simple..
26116                 st = '<style type="text/css">' +
26117                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26118                    '</style>';
26119         } else {
26120             for (var i in this.stylesheets) { 
26121                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26122             }
26123             
26124         }
26125         
26126         st +=  '<style type="text/css">' +
26127             'IMG { cursor: pointer } ' +
26128         '</style>';
26129
26130         var cls = 'roo-htmleditor-body';
26131         
26132         if(this.bodyCls.length){
26133             cls += ' ' + this.bodyCls;
26134         }
26135         
26136         return '<html><head>' + st  +
26137             //<style type="text/css">' +
26138             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26139             //'</style>' +
26140             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26141     },
26142
26143     // private
26144     onRender : function(ct, position)
26145     {
26146         var _t = this;
26147         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26148         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26149         
26150         
26151         this.el.dom.style.border = '0 none';
26152         this.el.dom.setAttribute('tabIndex', -1);
26153         this.el.addClass('x-hidden hide');
26154         
26155         
26156         
26157         if(Roo.isIE){ // fix IE 1px bogus margin
26158             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26159         }
26160        
26161         
26162         this.frameId = Roo.id();
26163         
26164          
26165         
26166         var iframe = this.owner.wrap.createChild({
26167             tag: 'iframe',
26168             cls: 'form-control', // bootstrap..
26169             id: this.frameId,
26170             name: this.frameId,
26171             frameBorder : 'no',
26172             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26173         }, this.el
26174         );
26175         
26176         
26177         this.iframe = iframe.dom;
26178
26179          this.assignDocWin();
26180         
26181         this.doc.designMode = 'on';
26182        
26183         this.doc.open();
26184         this.doc.write(this.getDocMarkup());
26185         this.doc.close();
26186
26187         
26188         var task = { // must defer to wait for browser to be ready
26189             run : function(){
26190                 //console.log("run task?" + this.doc.readyState);
26191                 this.assignDocWin();
26192                 if(this.doc.body || this.doc.readyState == 'complete'){
26193                     try {
26194                         this.doc.designMode="on";
26195                     } catch (e) {
26196                         return;
26197                     }
26198                     Roo.TaskMgr.stop(task);
26199                     this.initEditor.defer(10, this);
26200                 }
26201             },
26202             interval : 10,
26203             duration: 10000,
26204             scope: this
26205         };
26206         Roo.TaskMgr.start(task);
26207
26208     },
26209
26210     // private
26211     onResize : function(w, h)
26212     {
26213          Roo.log('resize: ' +w + ',' + h );
26214         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26215         if(!this.iframe){
26216             return;
26217         }
26218         if(typeof w == 'number'){
26219             
26220             this.iframe.style.width = w + 'px';
26221         }
26222         if(typeof h == 'number'){
26223             
26224             this.iframe.style.height = h + 'px';
26225             if(this.doc){
26226                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26227             }
26228         }
26229         
26230     },
26231
26232     /**
26233      * Toggles the editor between standard and source edit mode.
26234      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26235      */
26236     toggleSourceEdit : function(sourceEditMode){
26237         
26238         this.sourceEditMode = sourceEditMode === true;
26239         
26240         if(this.sourceEditMode){
26241  
26242             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26243             
26244         }else{
26245             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26246             //this.iframe.className = '';
26247             this.deferFocus();
26248         }
26249         //this.setSize(this.owner.wrap.getSize());
26250         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26251     },
26252
26253     
26254   
26255
26256     /**
26257      * Protected method that will not generally be called directly. If you need/want
26258      * custom HTML cleanup, this is the method you should override.
26259      * @param {String} html The HTML to be cleaned
26260      * return {String} The cleaned HTML
26261      */
26262     cleanHtml : function(html){
26263         html = String(html);
26264         if(html.length > 5){
26265             if(Roo.isSafari){ // strip safari nonsense
26266                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26267             }
26268         }
26269         if(html == '&nbsp;'){
26270             html = '';
26271         }
26272         return html;
26273     },
26274
26275     /**
26276      * HTML Editor -> Textarea
26277      * Protected method that will not generally be called directly. Syncs the contents
26278      * of the editor iframe with the textarea.
26279      */
26280     syncValue : function(){
26281         if(this.initialized){
26282             var bd = (this.doc.body || this.doc.documentElement);
26283             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26284             var html = bd.innerHTML;
26285             if(Roo.isSafari){
26286                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26287                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26288                 if(m && m[1]){
26289                     html = '<div style="'+m[0]+'">' + html + '</div>';
26290                 }
26291             }
26292             html = this.cleanHtml(html);
26293             // fix up the special chars.. normaly like back quotes in word...
26294             // however we do not want to do this with chinese..
26295             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26296                 
26297                 var cc = match.charCodeAt();
26298
26299                 // Get the character value, handling surrogate pairs
26300                 if (match.length == 2) {
26301                     // It's a surrogate pair, calculate the Unicode code point
26302                     var high = match.charCodeAt(0) - 0xD800;
26303                     var low  = match.charCodeAt(1) - 0xDC00;
26304                     cc = (high * 0x400) + low + 0x10000;
26305                 }  else if (
26306                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26307                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26308                     (cc >= 0xf900 && cc < 0xfb00 )
26309                 ) {
26310                         return match;
26311                 }  
26312          
26313                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26314                 return "&#" + cc + ";";
26315                 
26316                 
26317             });
26318             
26319             
26320              
26321             if(this.owner.fireEvent('beforesync', this, html) !== false){
26322                 this.el.dom.value = html;
26323                 this.owner.fireEvent('sync', this, html);
26324             }
26325         }
26326     },
26327
26328     /**
26329      * Protected method that will not generally be called directly. Pushes the value of the textarea
26330      * into the iframe editor.
26331      */
26332     pushValue : function(){
26333         if(this.initialized){
26334             var v = this.el.dom.value.trim();
26335             
26336 //            if(v.length < 1){
26337 //                v = '&#160;';
26338 //            }
26339             
26340             if(this.owner.fireEvent('beforepush', this, v) !== false){
26341                 var d = (this.doc.body || this.doc.documentElement);
26342                 d.innerHTML = v;
26343                 this.cleanUpPaste();
26344                 this.el.dom.value = d.innerHTML;
26345                 this.owner.fireEvent('push', this, v);
26346             }
26347         }
26348     },
26349
26350     // private
26351     deferFocus : function(){
26352         this.focus.defer(10, this);
26353     },
26354
26355     // doc'ed in Field
26356     focus : function(){
26357         if(this.win && !this.sourceEditMode){
26358             this.win.focus();
26359         }else{
26360             this.el.focus();
26361         }
26362     },
26363     
26364     assignDocWin: function()
26365     {
26366         var iframe = this.iframe;
26367         
26368          if(Roo.isIE){
26369             this.doc = iframe.contentWindow.document;
26370             this.win = iframe.contentWindow;
26371         } else {
26372 //            if (!Roo.get(this.frameId)) {
26373 //                return;
26374 //            }
26375 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26376 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26377             
26378             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26379                 return;
26380             }
26381             
26382             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26383             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26384         }
26385     },
26386     
26387     // private
26388     initEditor : function(){
26389         //console.log("INIT EDITOR");
26390         this.assignDocWin();
26391         
26392         
26393         
26394         this.doc.designMode="on";
26395         this.doc.open();
26396         this.doc.write(this.getDocMarkup());
26397         this.doc.close();
26398         
26399         var dbody = (this.doc.body || this.doc.documentElement);
26400         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26401         // this copies styles from the containing element into thsi one..
26402         // not sure why we need all of this..
26403         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26404         
26405         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26406         //ss['background-attachment'] = 'fixed'; // w3c
26407         dbody.bgProperties = 'fixed'; // ie
26408         //Roo.DomHelper.applyStyles(dbody, ss);
26409         Roo.EventManager.on(this.doc, {
26410             //'mousedown': this.onEditorEvent,
26411             'mouseup': this.onEditorEvent,
26412             'dblclick': this.onEditorEvent,
26413             'click': this.onEditorEvent,
26414             'keyup': this.onEditorEvent,
26415             buffer:100,
26416             scope: this
26417         });
26418         if(Roo.isGecko){
26419             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26420         }
26421         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26422             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26423         }
26424         this.initialized = true;
26425
26426         this.owner.fireEvent('initialize', this);
26427         this.pushValue();
26428     },
26429
26430     // private
26431     onDestroy : function(){
26432         
26433         
26434         
26435         if(this.rendered){
26436             
26437             //for (var i =0; i < this.toolbars.length;i++) {
26438             //    // fixme - ask toolbars for heights?
26439             //    this.toolbars[i].onDestroy();
26440            // }
26441             
26442             //this.wrap.dom.innerHTML = '';
26443             //this.wrap.remove();
26444         }
26445     },
26446
26447     // private
26448     onFirstFocus : function(){
26449         
26450         this.assignDocWin();
26451         
26452         
26453         this.activated = true;
26454          
26455     
26456         if(Roo.isGecko){ // prevent silly gecko errors
26457             this.win.focus();
26458             var s = this.win.getSelection();
26459             if(!s.focusNode || s.focusNode.nodeType != 3){
26460                 var r = s.getRangeAt(0);
26461                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26462                 r.collapse(true);
26463                 this.deferFocus();
26464             }
26465             try{
26466                 this.execCmd('useCSS', true);
26467                 this.execCmd('styleWithCSS', false);
26468             }catch(e){}
26469         }
26470         this.owner.fireEvent('activate', this);
26471     },
26472
26473     // private
26474     adjustFont: function(btn){
26475         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26476         //if(Roo.isSafari){ // safari
26477         //    adjust *= 2;
26478        // }
26479         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26480         if(Roo.isSafari){ // safari
26481             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26482             v =  (v < 10) ? 10 : v;
26483             v =  (v > 48) ? 48 : v;
26484             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26485             
26486         }
26487         
26488         
26489         v = Math.max(1, v+adjust);
26490         
26491         this.execCmd('FontSize', v  );
26492     },
26493
26494     onEditorEvent : function(e)
26495     {
26496         this.owner.fireEvent('editorevent', this, e);
26497       //  this.updateToolbar();
26498         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26499     },
26500
26501     insertTag : function(tg)
26502     {
26503         // could be a bit smarter... -> wrap the current selected tRoo..
26504         if (tg.toLowerCase() == 'span' ||
26505             tg.toLowerCase() == 'code' ||
26506             tg.toLowerCase() == 'sup' ||
26507             tg.toLowerCase() == 'sub' 
26508             ) {
26509             
26510             range = this.createRange(this.getSelection());
26511             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26512             wrappingNode.appendChild(range.extractContents());
26513             range.insertNode(wrappingNode);
26514
26515             return;
26516             
26517             
26518             
26519         }
26520         this.execCmd("formatblock",   tg);
26521         
26522     },
26523     
26524     insertText : function(txt)
26525     {
26526         
26527         
26528         var range = this.createRange();
26529         range.deleteContents();
26530                //alert(Sender.getAttribute('label'));
26531                
26532         range.insertNode(this.doc.createTextNode(txt));
26533     } ,
26534     
26535      
26536
26537     /**
26538      * Executes a Midas editor command on the editor document and performs necessary focus and
26539      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26540      * @param {String} cmd The Midas command
26541      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26542      */
26543     relayCmd : function(cmd, value){
26544         this.win.focus();
26545         this.execCmd(cmd, value);
26546         this.owner.fireEvent('editorevent', this);
26547         //this.updateToolbar();
26548         this.owner.deferFocus();
26549     },
26550
26551     /**
26552      * Executes a Midas editor command directly on the editor document.
26553      * For visual commands, you should use {@link #relayCmd} instead.
26554      * <b>This should only be called after the editor is initialized.</b>
26555      * @param {String} cmd The Midas command
26556      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26557      */
26558     execCmd : function(cmd, value){
26559         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26560         this.syncValue();
26561     },
26562  
26563  
26564    
26565     /**
26566      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26567      * to insert tRoo.
26568      * @param {String} text | dom node.. 
26569      */
26570     insertAtCursor : function(text)
26571     {
26572         
26573         if(!this.activated){
26574             return;
26575         }
26576         /*
26577         if(Roo.isIE){
26578             this.win.focus();
26579             var r = this.doc.selection.createRange();
26580             if(r){
26581                 r.collapse(true);
26582                 r.pasteHTML(text);
26583                 this.syncValue();
26584                 this.deferFocus();
26585             
26586             }
26587             return;
26588         }
26589         */
26590         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26591             this.win.focus();
26592             
26593             
26594             // from jquery ui (MIT licenced)
26595             var range, node;
26596             var win = this.win;
26597             
26598             if (win.getSelection && win.getSelection().getRangeAt) {
26599                 range = win.getSelection().getRangeAt(0);
26600                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26601                 range.insertNode(node);
26602             } else if (win.document.selection && win.document.selection.createRange) {
26603                 // no firefox support
26604                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26605                 win.document.selection.createRange().pasteHTML(txt);
26606             } else {
26607                 // no firefox support
26608                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26609                 this.execCmd('InsertHTML', txt);
26610             } 
26611             
26612             this.syncValue();
26613             
26614             this.deferFocus();
26615         }
26616     },
26617  // private
26618     mozKeyPress : function(e){
26619         if(e.ctrlKey){
26620             var c = e.getCharCode(), cmd;
26621           
26622             if(c > 0){
26623                 c = String.fromCharCode(c).toLowerCase();
26624                 switch(c){
26625                     case 'b':
26626                         cmd = 'bold';
26627                         break;
26628                     case 'i':
26629                         cmd = 'italic';
26630                         break;
26631                     
26632                     case 'u':
26633                         cmd = 'underline';
26634                         break;
26635                     
26636                     case 'v':
26637                         this.cleanUpPaste.defer(100, this);
26638                         return;
26639                         
26640                 }
26641                 if(cmd){
26642                     this.win.focus();
26643                     this.execCmd(cmd);
26644                     this.deferFocus();
26645                     e.preventDefault();
26646                 }
26647                 
26648             }
26649         }
26650     },
26651
26652     // private
26653     fixKeys : function(){ // load time branching for fastest keydown performance
26654         if(Roo.isIE){
26655             return function(e){
26656                 var k = e.getKey(), r;
26657                 if(k == e.TAB){
26658                     e.stopEvent();
26659                     r = this.doc.selection.createRange();
26660                     if(r){
26661                         r.collapse(true);
26662                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26663                         this.deferFocus();
26664                     }
26665                     return;
26666                 }
26667                 
26668                 if(k == e.ENTER){
26669                     r = this.doc.selection.createRange();
26670                     if(r){
26671                         var target = r.parentElement();
26672                         if(!target || target.tagName.toLowerCase() != 'li'){
26673                             e.stopEvent();
26674                             r.pasteHTML('<br />');
26675                             r.collapse(false);
26676                             r.select();
26677                         }
26678                     }
26679                 }
26680                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26681                     this.cleanUpPaste.defer(100, this);
26682                     return;
26683                 }
26684                 
26685                 
26686             };
26687         }else if(Roo.isOpera){
26688             return function(e){
26689                 var k = e.getKey();
26690                 if(k == e.TAB){
26691                     e.stopEvent();
26692                     this.win.focus();
26693                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26694                     this.deferFocus();
26695                 }
26696                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26697                     this.cleanUpPaste.defer(100, this);
26698                     return;
26699                 }
26700                 
26701             };
26702         }else if(Roo.isSafari){
26703             return function(e){
26704                 var k = e.getKey();
26705                 
26706                 if(k == e.TAB){
26707                     e.stopEvent();
26708                     this.execCmd('InsertText','\t');
26709                     this.deferFocus();
26710                     return;
26711                 }
26712                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26713                     this.cleanUpPaste.defer(100, this);
26714                     return;
26715                 }
26716                 
26717              };
26718         }
26719     }(),
26720     
26721     getAllAncestors: function()
26722     {
26723         var p = this.getSelectedNode();
26724         var a = [];
26725         if (!p) {
26726             a.push(p); // push blank onto stack..
26727             p = this.getParentElement();
26728         }
26729         
26730         
26731         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26732             a.push(p);
26733             p = p.parentNode;
26734         }
26735         a.push(this.doc.body);
26736         return a;
26737     },
26738     lastSel : false,
26739     lastSelNode : false,
26740     
26741     
26742     getSelection : function() 
26743     {
26744         this.assignDocWin();
26745         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26746     },
26747     
26748     getSelectedNode: function() 
26749     {
26750         // this may only work on Gecko!!!
26751         
26752         // should we cache this!!!!
26753         
26754         
26755         
26756          
26757         var range = this.createRange(this.getSelection()).cloneRange();
26758         
26759         if (Roo.isIE) {
26760             var parent = range.parentElement();
26761             while (true) {
26762                 var testRange = range.duplicate();
26763                 testRange.moveToElementText(parent);
26764                 if (testRange.inRange(range)) {
26765                     break;
26766                 }
26767                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26768                     break;
26769                 }
26770                 parent = parent.parentElement;
26771             }
26772             return parent;
26773         }
26774         
26775         // is ancestor a text element.
26776         var ac =  range.commonAncestorContainer;
26777         if (ac.nodeType == 3) {
26778             ac = ac.parentNode;
26779         }
26780         
26781         var ar = ac.childNodes;
26782          
26783         var nodes = [];
26784         var other_nodes = [];
26785         var has_other_nodes = false;
26786         for (var i=0;i<ar.length;i++) {
26787             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26788                 continue;
26789             }
26790             // fullly contained node.
26791             
26792             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26793                 nodes.push(ar[i]);
26794                 continue;
26795             }
26796             
26797             // probably selected..
26798             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26799                 other_nodes.push(ar[i]);
26800                 continue;
26801             }
26802             // outer..
26803             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26804                 continue;
26805             }
26806             
26807             
26808             has_other_nodes = true;
26809         }
26810         if (!nodes.length && other_nodes.length) {
26811             nodes= other_nodes;
26812         }
26813         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26814             return false;
26815         }
26816         
26817         return nodes[0];
26818     },
26819     createRange: function(sel)
26820     {
26821         // this has strange effects when using with 
26822         // top toolbar - not sure if it's a great idea.
26823         //this.editor.contentWindow.focus();
26824         if (typeof sel != "undefined") {
26825             try {
26826                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26827             } catch(e) {
26828                 return this.doc.createRange();
26829             }
26830         } else {
26831             return this.doc.createRange();
26832         }
26833     },
26834     getParentElement: function()
26835     {
26836         
26837         this.assignDocWin();
26838         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26839         
26840         var range = this.createRange(sel);
26841          
26842         try {
26843             var p = range.commonAncestorContainer;
26844             while (p.nodeType == 3) { // text node
26845                 p = p.parentNode;
26846             }
26847             return p;
26848         } catch (e) {
26849             return null;
26850         }
26851     
26852     },
26853     /***
26854      *
26855      * Range intersection.. the hard stuff...
26856      *  '-1' = before
26857      *  '0' = hits..
26858      *  '1' = after.
26859      *         [ -- selected range --- ]
26860      *   [fail]                        [fail]
26861      *
26862      *    basically..
26863      *      if end is before start or  hits it. fail.
26864      *      if start is after end or hits it fail.
26865      *
26866      *   if either hits (but other is outside. - then it's not 
26867      *   
26868      *    
26869      **/
26870     
26871     
26872     // @see http://www.thismuchiknow.co.uk/?p=64.
26873     rangeIntersectsNode : function(range, node)
26874     {
26875         var nodeRange = node.ownerDocument.createRange();
26876         try {
26877             nodeRange.selectNode(node);
26878         } catch (e) {
26879             nodeRange.selectNodeContents(node);
26880         }
26881     
26882         var rangeStartRange = range.cloneRange();
26883         rangeStartRange.collapse(true);
26884     
26885         var rangeEndRange = range.cloneRange();
26886         rangeEndRange.collapse(false);
26887     
26888         var nodeStartRange = nodeRange.cloneRange();
26889         nodeStartRange.collapse(true);
26890     
26891         var nodeEndRange = nodeRange.cloneRange();
26892         nodeEndRange.collapse(false);
26893     
26894         return rangeStartRange.compareBoundaryPoints(
26895                  Range.START_TO_START, nodeEndRange) == -1 &&
26896                rangeEndRange.compareBoundaryPoints(
26897                  Range.START_TO_START, nodeStartRange) == 1;
26898         
26899          
26900     },
26901     rangeCompareNode : function(range, node)
26902     {
26903         var nodeRange = node.ownerDocument.createRange();
26904         try {
26905             nodeRange.selectNode(node);
26906         } catch (e) {
26907             nodeRange.selectNodeContents(node);
26908         }
26909         
26910         
26911         range.collapse(true);
26912     
26913         nodeRange.collapse(true);
26914      
26915         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26916         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26917          
26918         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26919         
26920         var nodeIsBefore   =  ss == 1;
26921         var nodeIsAfter    = ee == -1;
26922         
26923         if (nodeIsBefore && nodeIsAfter) {
26924             return 0; // outer
26925         }
26926         if (!nodeIsBefore && nodeIsAfter) {
26927             return 1; //right trailed.
26928         }
26929         
26930         if (nodeIsBefore && !nodeIsAfter) {
26931             return 2;  // left trailed.
26932         }
26933         // fully contined.
26934         return 3;
26935     },
26936
26937     // private? - in a new class?
26938     cleanUpPaste :  function()
26939     {
26940         // cleans up the whole document..
26941         Roo.log('cleanuppaste');
26942         
26943         this.cleanUpChildren(this.doc.body);
26944         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26945         if (clean != this.doc.body.innerHTML) {
26946             this.doc.body.innerHTML = clean;
26947         }
26948         
26949     },
26950     
26951     cleanWordChars : function(input) {// change the chars to hex code
26952         var he = Roo.HtmlEditorCore;
26953         
26954         var output = input;
26955         Roo.each(he.swapCodes, function(sw) { 
26956             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26957             
26958             output = output.replace(swapper, sw[1]);
26959         });
26960         
26961         return output;
26962     },
26963     
26964     
26965     cleanUpChildren : function (n)
26966     {
26967         if (!n.childNodes.length) {
26968             return;
26969         }
26970         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26971            this.cleanUpChild(n.childNodes[i]);
26972         }
26973     },
26974     
26975     
26976         
26977     
26978     cleanUpChild : function (node)
26979     {
26980         var ed = this;
26981         //console.log(node);
26982         if (node.nodeName == "#text") {
26983             // clean up silly Windows -- stuff?
26984             return; 
26985         }
26986         if (node.nodeName == "#comment") {
26987             if (!this.allowComments) {
26988                 node.parentNode.removeChild(node);
26989             }
26990             // clean up silly Windows -- stuff?
26991             return; 
26992         }
26993         var lcname = node.tagName.toLowerCase();
26994         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26995         // whitelist of tags..
26996         
26997         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26998             // remove node.
26999             node.parentNode.removeChild(node);
27000             return;
27001             
27002         }
27003         
27004         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27005         
27006         // spans with no attributes - just remove them..
27007         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27008             remove_keep_children = true;
27009         }
27010         
27011         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27012         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27013         
27014         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27015         //    remove_keep_children = true;
27016         //}
27017         
27018         if (remove_keep_children) {
27019             this.cleanUpChildren(node);
27020             // inserts everything just before this node...
27021             while (node.childNodes.length) {
27022                 var cn = node.childNodes[0];
27023                 node.removeChild(cn);
27024                 node.parentNode.insertBefore(cn, node);
27025             }
27026             node.parentNode.removeChild(node);
27027             return;
27028         }
27029         
27030         if (!node.attributes || !node.attributes.length) {
27031             
27032           
27033             
27034             
27035             this.cleanUpChildren(node);
27036             return;
27037         }
27038         
27039         function cleanAttr(n,v)
27040         {
27041             
27042             if (v.match(/^\./) || v.match(/^\//)) {
27043                 return;
27044             }
27045             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27046                 return;
27047             }
27048             if (v.match(/^#/)) {
27049                 return;
27050             }
27051             if (v.match(/^\{/)) { // allow template editing.
27052                 return;
27053             }
27054 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27055             node.removeAttribute(n);
27056             
27057         }
27058         
27059         var cwhite = this.cwhite;
27060         var cblack = this.cblack;
27061             
27062         function cleanStyle(n,v)
27063         {
27064             if (v.match(/expression/)) { //XSS?? should we even bother..
27065                 node.removeAttribute(n);
27066                 return;
27067             }
27068             
27069             var parts = v.split(/;/);
27070             var clean = [];
27071             
27072             Roo.each(parts, function(p) {
27073                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27074                 if (!p.length) {
27075                     return true;
27076                 }
27077                 var l = p.split(':').shift().replace(/\s+/g,'');
27078                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27079                 
27080                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27081 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27082                     //node.removeAttribute(n);
27083                     return true;
27084                 }
27085                 //Roo.log()
27086                 // only allow 'c whitelisted system attributes'
27087                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27088 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27089                     //node.removeAttribute(n);
27090                     return true;
27091                 }
27092                 
27093                 
27094                  
27095                 
27096                 clean.push(p);
27097                 return true;
27098             });
27099             if (clean.length) { 
27100                 node.setAttribute(n, clean.join(';'));
27101             } else {
27102                 node.removeAttribute(n);
27103             }
27104             
27105         }
27106         
27107         
27108         for (var i = node.attributes.length-1; i > -1 ; i--) {
27109             var a = node.attributes[i];
27110             //console.log(a);
27111             
27112             if (a.name.toLowerCase().substr(0,2)=='on')  {
27113                 node.removeAttribute(a.name);
27114                 continue;
27115             }
27116             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27117                 node.removeAttribute(a.name);
27118                 continue;
27119             }
27120             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27121                 cleanAttr(a.name,a.value); // fixme..
27122                 continue;
27123             }
27124             if (a.name == 'style') {
27125                 cleanStyle(a.name,a.value);
27126                 continue;
27127             }
27128             /// clean up MS crap..
27129             // tecnically this should be a list of valid class'es..
27130             
27131             
27132             if (a.name == 'class') {
27133                 if (a.value.match(/^Mso/)) {
27134                     node.removeAttribute('class');
27135                 }
27136                 
27137                 if (a.value.match(/^body$/)) {
27138                     node.removeAttribute('class');
27139                 }
27140                 continue;
27141             }
27142             
27143             // style cleanup!?
27144             // class cleanup?
27145             
27146         }
27147         
27148         
27149         this.cleanUpChildren(node);
27150         
27151         
27152     },
27153     
27154     /**
27155      * Clean up MS wordisms...
27156      */
27157     cleanWord : function(node)
27158     {
27159         if (!node) {
27160             this.cleanWord(this.doc.body);
27161             return;
27162         }
27163         
27164         if(
27165                 node.nodeName == 'SPAN' &&
27166                 !node.hasAttributes() &&
27167                 node.childNodes.length == 1 &&
27168                 node.firstChild.nodeName == "#text"  
27169         ) {
27170             var textNode = node.firstChild;
27171             node.removeChild(textNode);
27172             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27173                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27174             }
27175             node.parentNode.insertBefore(textNode, node);
27176             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27177                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27178             }
27179             node.parentNode.removeChild(node);
27180         }
27181         
27182         if (node.nodeName == "#text") {
27183             // clean up silly Windows -- stuff?
27184             return; 
27185         }
27186         if (node.nodeName == "#comment") {
27187             node.parentNode.removeChild(node);
27188             // clean up silly Windows -- stuff?
27189             return; 
27190         }
27191         
27192         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27193             node.parentNode.removeChild(node);
27194             return;
27195         }
27196         //Roo.log(node.tagName);
27197         // remove - but keep children..
27198         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27199             //Roo.log('-- removed');
27200             while (node.childNodes.length) {
27201                 var cn = node.childNodes[0];
27202                 node.removeChild(cn);
27203                 node.parentNode.insertBefore(cn, node);
27204                 // move node to parent - and clean it..
27205                 this.cleanWord(cn);
27206             }
27207             node.parentNode.removeChild(node);
27208             /// no need to iterate chidlren = it's got none..
27209             //this.iterateChildren(node, this.cleanWord);
27210             return;
27211         }
27212         // clean styles
27213         if (node.className.length) {
27214             
27215             var cn = node.className.split(/\W+/);
27216             var cna = [];
27217             Roo.each(cn, function(cls) {
27218                 if (cls.match(/Mso[a-zA-Z]+/)) {
27219                     return;
27220                 }
27221                 cna.push(cls);
27222             });
27223             node.className = cna.length ? cna.join(' ') : '';
27224             if (!cna.length) {
27225                 node.removeAttribute("class");
27226             }
27227         }
27228         
27229         if (node.hasAttribute("lang")) {
27230             node.removeAttribute("lang");
27231         }
27232         
27233         if (node.hasAttribute("style")) {
27234             
27235             var styles = node.getAttribute("style").split(";");
27236             var nstyle = [];
27237             Roo.each(styles, function(s) {
27238                 if (!s.match(/:/)) {
27239                     return;
27240                 }
27241                 var kv = s.split(":");
27242                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27243                     return;
27244                 }
27245                 // what ever is left... we allow.
27246                 nstyle.push(s);
27247             });
27248             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27249             if (!nstyle.length) {
27250                 node.removeAttribute('style');
27251             }
27252         }
27253         this.iterateChildren(node, this.cleanWord);
27254         
27255         
27256         
27257     },
27258     /**
27259      * iterateChildren of a Node, calling fn each time, using this as the scole..
27260      * @param {DomNode} node node to iterate children of.
27261      * @param {Function} fn method of this class to call on each item.
27262      */
27263     iterateChildren : function(node, fn)
27264     {
27265         if (!node.childNodes.length) {
27266                 return;
27267         }
27268         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27269            fn.call(this, node.childNodes[i])
27270         }
27271     },
27272     
27273     
27274     /**
27275      * cleanTableWidths.
27276      *
27277      * Quite often pasting from word etc.. results in tables with column and widths.
27278      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27279      *
27280      */
27281     cleanTableWidths : function(node)
27282     {
27283          
27284          
27285         if (!node) {
27286             this.cleanTableWidths(this.doc.body);
27287             return;
27288         }
27289         
27290         // ignore list...
27291         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27292             return; 
27293         }
27294         Roo.log(node.tagName);
27295         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27296             this.iterateChildren(node, this.cleanTableWidths);
27297             return;
27298         }
27299         if (node.hasAttribute('width')) {
27300             node.removeAttribute('width');
27301         }
27302         
27303          
27304         if (node.hasAttribute("style")) {
27305             // pretty basic...
27306             
27307             var styles = node.getAttribute("style").split(";");
27308             var nstyle = [];
27309             Roo.each(styles, function(s) {
27310                 if (!s.match(/:/)) {
27311                     return;
27312                 }
27313                 var kv = s.split(":");
27314                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27315                     return;
27316                 }
27317                 // what ever is left... we allow.
27318                 nstyle.push(s);
27319             });
27320             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27321             if (!nstyle.length) {
27322                 node.removeAttribute('style');
27323             }
27324         }
27325         
27326         this.iterateChildren(node, this.cleanTableWidths);
27327         
27328         
27329     },
27330     
27331     
27332     
27333     
27334     domToHTML : function(currentElement, depth, nopadtext) {
27335         
27336         depth = depth || 0;
27337         nopadtext = nopadtext || false;
27338     
27339         if (!currentElement) {
27340             return this.domToHTML(this.doc.body);
27341         }
27342         
27343         //Roo.log(currentElement);
27344         var j;
27345         var allText = false;
27346         var nodeName = currentElement.nodeName;
27347         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27348         
27349         if  (nodeName == '#text') {
27350             
27351             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27352         }
27353         
27354         
27355         var ret = '';
27356         if (nodeName != 'BODY') {
27357              
27358             var i = 0;
27359             // Prints the node tagName, such as <A>, <IMG>, etc
27360             if (tagName) {
27361                 var attr = [];
27362                 for(i = 0; i < currentElement.attributes.length;i++) {
27363                     // quoting?
27364                     var aname = currentElement.attributes.item(i).name;
27365                     if (!currentElement.attributes.item(i).value.length) {
27366                         continue;
27367                     }
27368                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27369                 }
27370                 
27371                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27372             } 
27373             else {
27374                 
27375                 // eack
27376             }
27377         } else {
27378             tagName = false;
27379         }
27380         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27381             return ret;
27382         }
27383         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27384             nopadtext = true;
27385         }
27386         
27387         
27388         // Traverse the tree
27389         i = 0;
27390         var currentElementChild = currentElement.childNodes.item(i);
27391         var allText = true;
27392         var innerHTML  = '';
27393         lastnode = '';
27394         while (currentElementChild) {
27395             // Formatting code (indent the tree so it looks nice on the screen)
27396             var nopad = nopadtext;
27397             if (lastnode == 'SPAN') {
27398                 nopad  = true;
27399             }
27400             // text
27401             if  (currentElementChild.nodeName == '#text') {
27402                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27403                 toadd = nopadtext ? toadd : toadd.trim();
27404                 if (!nopad && toadd.length > 80) {
27405                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27406                 }
27407                 innerHTML  += toadd;
27408                 
27409                 i++;
27410                 currentElementChild = currentElement.childNodes.item(i);
27411                 lastNode = '';
27412                 continue;
27413             }
27414             allText = false;
27415             
27416             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27417                 
27418             // Recursively traverse the tree structure of the child node
27419             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27420             lastnode = currentElementChild.nodeName;
27421             i++;
27422             currentElementChild=currentElement.childNodes.item(i);
27423         }
27424         
27425         ret += innerHTML;
27426         
27427         if (!allText) {
27428                 // The remaining code is mostly for formatting the tree
27429             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27430         }
27431         
27432         
27433         if (tagName) {
27434             ret+= "</"+tagName+">";
27435         }
27436         return ret;
27437         
27438     },
27439         
27440     applyBlacklists : function()
27441     {
27442         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27443         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27444         
27445         this.white = [];
27446         this.black = [];
27447         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27448             if (b.indexOf(tag) > -1) {
27449                 return;
27450             }
27451             this.white.push(tag);
27452             
27453         }, this);
27454         
27455         Roo.each(w, function(tag) {
27456             if (b.indexOf(tag) > -1) {
27457                 return;
27458             }
27459             if (this.white.indexOf(tag) > -1) {
27460                 return;
27461             }
27462             this.white.push(tag);
27463             
27464         }, this);
27465         
27466         
27467         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27468             if (w.indexOf(tag) > -1) {
27469                 return;
27470             }
27471             this.black.push(tag);
27472             
27473         }, this);
27474         
27475         Roo.each(b, function(tag) {
27476             if (w.indexOf(tag) > -1) {
27477                 return;
27478             }
27479             if (this.black.indexOf(tag) > -1) {
27480                 return;
27481             }
27482             this.black.push(tag);
27483             
27484         }, this);
27485         
27486         
27487         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27488         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27489         
27490         this.cwhite = [];
27491         this.cblack = [];
27492         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27493             if (b.indexOf(tag) > -1) {
27494                 return;
27495             }
27496             this.cwhite.push(tag);
27497             
27498         }, this);
27499         
27500         Roo.each(w, function(tag) {
27501             if (b.indexOf(tag) > -1) {
27502                 return;
27503             }
27504             if (this.cwhite.indexOf(tag) > -1) {
27505                 return;
27506             }
27507             this.cwhite.push(tag);
27508             
27509         }, this);
27510         
27511         
27512         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27513             if (w.indexOf(tag) > -1) {
27514                 return;
27515             }
27516             this.cblack.push(tag);
27517             
27518         }, this);
27519         
27520         Roo.each(b, function(tag) {
27521             if (w.indexOf(tag) > -1) {
27522                 return;
27523             }
27524             if (this.cblack.indexOf(tag) > -1) {
27525                 return;
27526             }
27527             this.cblack.push(tag);
27528             
27529         }, this);
27530     },
27531     
27532     setStylesheets : function(stylesheets)
27533     {
27534         if(typeof(stylesheets) == 'string'){
27535             Roo.get(this.iframe.contentDocument.head).createChild({
27536                 tag : 'link',
27537                 rel : 'stylesheet',
27538                 type : 'text/css',
27539                 href : stylesheets
27540             });
27541             
27542             return;
27543         }
27544         var _this = this;
27545      
27546         Roo.each(stylesheets, function(s) {
27547             if(!s.length){
27548                 return;
27549             }
27550             
27551             Roo.get(_this.iframe.contentDocument.head).createChild({
27552                 tag : 'link',
27553                 rel : 'stylesheet',
27554                 type : 'text/css',
27555                 href : s
27556             });
27557         });
27558
27559         
27560     },
27561     
27562     removeStylesheets : function()
27563     {
27564         var _this = this;
27565         
27566         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27567             s.remove();
27568         });
27569     },
27570     
27571     setStyle : function(style)
27572     {
27573         Roo.get(this.iframe.contentDocument.head).createChild({
27574             tag : 'style',
27575             type : 'text/css',
27576             html : style
27577         });
27578
27579         return;
27580     }
27581     
27582     // hide stuff that is not compatible
27583     /**
27584      * @event blur
27585      * @hide
27586      */
27587     /**
27588      * @event change
27589      * @hide
27590      */
27591     /**
27592      * @event focus
27593      * @hide
27594      */
27595     /**
27596      * @event specialkey
27597      * @hide
27598      */
27599     /**
27600      * @cfg {String} fieldClass @hide
27601      */
27602     /**
27603      * @cfg {String} focusClass @hide
27604      */
27605     /**
27606      * @cfg {String} autoCreate @hide
27607      */
27608     /**
27609      * @cfg {String} inputType @hide
27610      */
27611     /**
27612      * @cfg {String} invalidClass @hide
27613      */
27614     /**
27615      * @cfg {String} invalidText @hide
27616      */
27617     /**
27618      * @cfg {String} msgFx @hide
27619      */
27620     /**
27621      * @cfg {String} validateOnBlur @hide
27622      */
27623 });
27624
27625 Roo.HtmlEditorCore.white = [
27626         'area', 'br', 'img', 'input', 'hr', 'wbr',
27627         
27628        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27629        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27630        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27631        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27632        'table',   'ul',         'xmp', 
27633        
27634        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27635       'thead',   'tr', 
27636      
27637       'dir', 'menu', 'ol', 'ul', 'dl',
27638        
27639       'embed',  'object'
27640 ];
27641
27642
27643 Roo.HtmlEditorCore.black = [
27644     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27645         'applet', // 
27646         'base',   'basefont', 'bgsound', 'blink',  'body', 
27647         'frame',  'frameset', 'head',    'html',   'ilayer', 
27648         'iframe', 'layer',  'link',     'meta',    'object',   
27649         'script', 'style' ,'title',  'xml' // clean later..
27650 ];
27651 Roo.HtmlEditorCore.clean = [
27652     'script', 'style', 'title', 'xml'
27653 ];
27654 Roo.HtmlEditorCore.remove = [
27655     'font'
27656 ];
27657 // attributes..
27658
27659 Roo.HtmlEditorCore.ablack = [
27660     'on'
27661 ];
27662     
27663 Roo.HtmlEditorCore.aclean = [ 
27664     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27665 ];
27666
27667 // protocols..
27668 Roo.HtmlEditorCore.pwhite= [
27669         'http',  'https',  'mailto'
27670 ];
27671
27672 // white listed style attributes.
27673 Roo.HtmlEditorCore.cwhite= [
27674       //  'text-align', /// default is to allow most things..
27675       
27676          
27677 //        'font-size'//??
27678 ];
27679
27680 // black listed style attributes.
27681 Roo.HtmlEditorCore.cblack= [
27682       //  'font-size' -- this can be set by the project 
27683 ];
27684
27685
27686 Roo.HtmlEditorCore.swapCodes   =[ 
27687     [    8211, "&#8211;" ], 
27688     [    8212, "&#8212;" ], 
27689     [    8216,  "'" ],  
27690     [    8217, "'" ],  
27691     [    8220, '"' ],  
27692     [    8221, '"' ],  
27693     [    8226, "*" ],  
27694     [    8230, "..." ]
27695 ]; 
27696
27697     /*
27698  * - LGPL
27699  *
27700  * HtmlEditor
27701  * 
27702  */
27703
27704 /**
27705  * @class Roo.bootstrap.form.HtmlEditor
27706  * @extends Roo.bootstrap.form.TextArea
27707  * Bootstrap HtmlEditor class
27708
27709  * @constructor
27710  * Create a new HtmlEditor
27711  * @param {Object} config The config object
27712  */
27713
27714 Roo.bootstrap.form.HtmlEditor = function(config){
27715     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27716     if (!this.toolbars) {
27717         this.toolbars = [];
27718     }
27719     
27720     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27721     this.addEvents({
27722             /**
27723              * @event initialize
27724              * Fires when the editor is fully initialized (including the iframe)
27725              * @param {HtmlEditor} this
27726              */
27727             initialize: true,
27728             /**
27729              * @event activate
27730              * Fires when the editor is first receives the focus. Any insertion must wait
27731              * until after this event.
27732              * @param {HtmlEditor} this
27733              */
27734             activate: true,
27735              /**
27736              * @event beforesync
27737              * Fires before the textarea is updated with content from the editor iframe. Return false
27738              * to cancel the sync.
27739              * @param {HtmlEditor} this
27740              * @param {String} html
27741              */
27742             beforesync: true,
27743              /**
27744              * @event beforepush
27745              * Fires before the iframe editor is updated with content from the textarea. Return false
27746              * to cancel the push.
27747              * @param {HtmlEditor} this
27748              * @param {String} html
27749              */
27750             beforepush: true,
27751              /**
27752              * @event sync
27753              * Fires when the textarea is updated with content from the editor iframe.
27754              * @param {HtmlEditor} this
27755              * @param {String} html
27756              */
27757             sync: true,
27758              /**
27759              * @event push
27760              * Fires when the iframe editor is updated with content from the textarea.
27761              * @param {HtmlEditor} this
27762              * @param {String} html
27763              */
27764             push: true,
27765              /**
27766              * @event editmodechange
27767              * Fires when the editor switches edit modes
27768              * @param {HtmlEditor} this
27769              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27770              */
27771             editmodechange: true,
27772             /**
27773              * @event editorevent
27774              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27775              * @param {HtmlEditor} this
27776              */
27777             editorevent: true,
27778             /**
27779              * @event firstfocus
27780              * Fires when on first focus - needed by toolbars..
27781              * @param {HtmlEditor} this
27782              */
27783             firstfocus: true,
27784             /**
27785              * @event autosave
27786              * Auto save the htmlEditor value as a file into Events
27787              * @param {HtmlEditor} this
27788              */
27789             autosave: true,
27790             /**
27791              * @event savedpreview
27792              * preview the saved version of htmlEditor
27793              * @param {HtmlEditor} this
27794              */
27795             savedpreview: true
27796         });
27797 };
27798
27799
27800 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27801     
27802     
27803       /**
27804      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27805      */
27806     toolbars : false,
27807     
27808      /**
27809     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27810     */
27811     btns : [],
27812    
27813      /**
27814      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27815      *                        Roo.resizable.
27816      */
27817     resizable : false,
27818      /**
27819      * @cfg {Number} height (in pixels)
27820      */   
27821     height: 300,
27822    /**
27823      * @cfg {Number} width (in pixels)
27824      */   
27825     width: false,
27826     
27827     /**
27828      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27829      * 
27830      */
27831     stylesheets: false,
27832     
27833     // id of frame..
27834     frameId: false,
27835     
27836     // private properties
27837     validationEvent : false,
27838     deferHeight: true,
27839     initialized : false,
27840     activated : false,
27841     
27842     onFocus : Roo.emptyFn,
27843     iframePad:3,
27844     hideMode:'offsets',
27845     
27846     tbContainer : false,
27847     
27848     bodyCls : '',
27849     
27850     toolbarContainer :function() {
27851         return this.wrap.select('.x-html-editor-tb',true).first();
27852     },
27853
27854     /**
27855      * Protected method that will not generally be called directly. It
27856      * is called when the editor creates its toolbar. Override this method if you need to
27857      * add custom toolbar buttons.
27858      * @param {HtmlEditor} editor
27859      */
27860     createToolbar : function(){
27861         Roo.log('renewing');
27862         Roo.log("create toolbars");
27863         
27864         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27865         this.toolbars[0].render(this.toolbarContainer());
27866         
27867         return;
27868         
27869 //        if (!editor.toolbars || !editor.toolbars.length) {
27870 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27871 //        }
27872 //        
27873 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27874 //            editor.toolbars[i] = Roo.factory(
27875 //                    typeof(editor.toolbars[i]) == 'string' ?
27876 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27877 //                Roo.bootstrap.form.HtmlEditor);
27878 //            editor.toolbars[i].init(editor);
27879 //        }
27880     },
27881
27882      
27883     // private
27884     onRender : function(ct, position)
27885     {
27886        // Roo.log("Call onRender: " + this.xtype);
27887         var _t = this;
27888         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27889       
27890         this.wrap = this.inputEl().wrap({
27891             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27892         });
27893         
27894         this.editorcore.onRender(ct, position);
27895          
27896         if (this.resizable) {
27897             this.resizeEl = new Roo.Resizable(this.wrap, {
27898                 pinned : true,
27899                 wrap: true,
27900                 dynamic : true,
27901                 minHeight : this.height,
27902                 height: this.height,
27903                 handles : this.resizable,
27904                 width: this.width,
27905                 listeners : {
27906                     resize : function(r, w, h) {
27907                         _t.onResize(w,h); // -something
27908                     }
27909                 }
27910             });
27911             
27912         }
27913         this.createToolbar(this);
27914        
27915         
27916         if(!this.width && this.resizable){
27917             this.setSize(this.wrap.getSize());
27918         }
27919         if (this.resizeEl) {
27920             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27921             // should trigger onReize..
27922         }
27923         
27924     },
27925
27926     // private
27927     onResize : function(w, h)
27928     {
27929         Roo.log('resize: ' +w + ',' + h );
27930         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27931         var ew = false;
27932         var eh = false;
27933         
27934         if(this.inputEl() ){
27935             if(typeof w == 'number'){
27936                 var aw = w - this.wrap.getFrameWidth('lr');
27937                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27938                 ew = aw;
27939             }
27940             if(typeof h == 'number'){
27941                  var tbh = -11;  // fixme it needs to tool bar size!
27942                 for (var i =0; i < this.toolbars.length;i++) {
27943                     // fixme - ask toolbars for heights?
27944                     tbh += this.toolbars[i].el.getHeight();
27945                     //if (this.toolbars[i].footer) {
27946                     //    tbh += this.toolbars[i].footer.el.getHeight();
27947                     //}
27948                 }
27949               
27950                 
27951                 
27952                 
27953                 
27954                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27955                 ah -= 5; // knock a few pixes off for look..
27956                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27957                 var eh = ah;
27958             }
27959         }
27960         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27961         this.editorcore.onResize(ew,eh);
27962         
27963     },
27964
27965     /**
27966      * Toggles the editor between standard and source edit mode.
27967      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27968      */
27969     toggleSourceEdit : function(sourceEditMode)
27970     {
27971         this.editorcore.toggleSourceEdit(sourceEditMode);
27972         
27973         if(this.editorcore.sourceEditMode){
27974             Roo.log('editor - showing textarea');
27975             
27976 //            Roo.log('in');
27977 //            Roo.log(this.syncValue());
27978             this.syncValue();
27979             this.inputEl().removeClass(['hide', 'x-hidden']);
27980             this.inputEl().dom.removeAttribute('tabIndex');
27981             this.inputEl().focus();
27982         }else{
27983             Roo.log('editor - hiding textarea');
27984 //            Roo.log('out')
27985 //            Roo.log(this.pushValue()); 
27986             this.pushValue();
27987             
27988             this.inputEl().addClass(['hide', 'x-hidden']);
27989             this.inputEl().dom.setAttribute('tabIndex', -1);
27990             //this.deferFocus();
27991         }
27992          
27993         if(this.resizable){
27994             this.setSize(this.wrap.getSize());
27995         }
27996         
27997         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27998     },
27999  
28000     // private (for BoxComponent)
28001     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28002
28003     // private (for BoxComponent)
28004     getResizeEl : function(){
28005         return this.wrap;
28006     },
28007
28008     // private (for BoxComponent)
28009     getPositionEl : function(){
28010         return this.wrap;
28011     },
28012
28013     // private
28014     initEvents : function(){
28015         this.originalValue = this.getValue();
28016     },
28017
28018 //    /**
28019 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28020 //     * @method
28021 //     */
28022 //    markInvalid : Roo.emptyFn,
28023 //    /**
28024 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28025 //     * @method
28026 //     */
28027 //    clearInvalid : Roo.emptyFn,
28028
28029     setValue : function(v){
28030         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28031         this.editorcore.pushValue();
28032     },
28033
28034      
28035     // private
28036     deferFocus : function(){
28037         this.focus.defer(10, this);
28038     },
28039
28040     // doc'ed in Field
28041     focus : function(){
28042         this.editorcore.focus();
28043         
28044     },
28045       
28046
28047     // private
28048     onDestroy : function(){
28049         
28050         
28051         
28052         if(this.rendered){
28053             
28054             for (var i =0; i < this.toolbars.length;i++) {
28055                 // fixme - ask toolbars for heights?
28056                 this.toolbars[i].onDestroy();
28057             }
28058             
28059             this.wrap.dom.innerHTML = '';
28060             this.wrap.remove();
28061         }
28062     },
28063
28064     // private
28065     onFirstFocus : function(){
28066         //Roo.log("onFirstFocus");
28067         this.editorcore.onFirstFocus();
28068          for (var i =0; i < this.toolbars.length;i++) {
28069             this.toolbars[i].onFirstFocus();
28070         }
28071         
28072     },
28073     
28074     // private
28075     syncValue : function()
28076     {   
28077         this.editorcore.syncValue();
28078     },
28079     
28080     pushValue : function()
28081     {   
28082         this.editorcore.pushValue();
28083     }
28084      
28085     
28086     // hide stuff that is not compatible
28087     /**
28088      * @event blur
28089      * @hide
28090      */
28091     /**
28092      * @event change
28093      * @hide
28094      */
28095     /**
28096      * @event focus
28097      * @hide
28098      */
28099     /**
28100      * @event specialkey
28101      * @hide
28102      */
28103     /**
28104      * @cfg {String} fieldClass @hide
28105      */
28106     /**
28107      * @cfg {String} focusClass @hide
28108      */
28109     /**
28110      * @cfg {String} autoCreate @hide
28111      */
28112     /**
28113      * @cfg {String} inputType @hide
28114      */
28115      
28116     /**
28117      * @cfg {String} invalidText @hide
28118      */
28119     /**
28120      * @cfg {String} msgFx @hide
28121      */
28122     /**
28123      * @cfg {String} validateOnBlur @hide
28124      */
28125 });
28126  
28127     
28128    
28129    
28130    
28131       
28132 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28133 /**
28134  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28135  * @parent Roo.bootstrap.form.HtmlEditor
28136  * @extends Roo.bootstrap.nav.Simplebar
28137  * Basic Toolbar
28138  * 
28139  * @example
28140  * Usage:
28141  *
28142  new Roo.bootstrap.form.HtmlEditor({
28143     ....
28144     toolbars : [
28145         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28146             disable : { fonts: 1 , format: 1, ..., ... , ...],
28147             btns : [ .... ]
28148         })
28149     }
28150      
28151  * 
28152  * @cfg {Object} disable List of elements to disable..
28153  * @cfg {Array} btns List of additional buttons.
28154  * 
28155  * 
28156  * NEEDS Extra CSS? 
28157  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28158  */
28159  
28160 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28161 {
28162     
28163     Roo.apply(this, config);
28164     
28165     // default disabled, based on 'good practice'..
28166     this.disable = this.disable || {};
28167     Roo.applyIf(this.disable, {
28168         fontSize : true,
28169         colors : true,
28170         specialElements : true
28171     });
28172     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28173     
28174     this.editor = config.editor;
28175     this.editorcore = config.editor.editorcore;
28176     
28177     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28178     
28179     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28180     // dont call parent... till later.
28181 }
28182 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28183      
28184     bar : true,
28185     
28186     editor : false,
28187     editorcore : false,
28188     
28189     
28190     formats : [
28191         "p" ,  
28192         "h1","h2","h3","h4","h5","h6", 
28193         "pre", "code", 
28194         "abbr", "acronym", "address", "cite", "samp", "var",
28195         'div','span'
28196     ],
28197     
28198     onRender : function(ct, position)
28199     {
28200        // Roo.log("Call onRender: " + this.xtype);
28201         
28202        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28203        Roo.log(this.el);
28204        this.el.dom.style.marginBottom = '0';
28205        var _this = this;
28206        var editorcore = this.editorcore;
28207        var editor= this.editor;
28208        
28209        var children = [];
28210        var btn = function(id,cmd , toggle, handler, html){
28211        
28212             var  event = toggle ? 'toggle' : 'click';
28213        
28214             var a = {
28215                 size : 'sm',
28216                 xtype: 'Button',
28217                 xns: Roo.bootstrap,
28218                 //glyphicon : id,
28219                 fa: id,
28220                 cmd : id || cmd,
28221                 enableToggle:toggle !== false,
28222                 html : html || '',
28223                 pressed : toggle ? false : null,
28224                 listeners : {}
28225             };
28226             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28227                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28228             };
28229             children.push(a);
28230             return a;
28231        }
28232        
28233     //    var cb_box = function...
28234         
28235         var style = {
28236                 xtype: 'Button',
28237                 size : 'sm',
28238                 xns: Roo.bootstrap,
28239                 fa : 'font',
28240                 //html : 'submit'
28241                 menu : {
28242                     xtype: 'Menu',
28243                     xns: Roo.bootstrap,
28244                     items:  []
28245                 }
28246         };
28247         Roo.each(this.formats, function(f) {
28248             style.menu.items.push({
28249                 xtype :'MenuItem',
28250                 xns: Roo.bootstrap,
28251                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28252                 tagname : f,
28253                 listeners : {
28254                     click : function()
28255                     {
28256                         editorcore.insertTag(this.tagname);
28257                         editor.focus();
28258                     }
28259                 }
28260                 
28261             });
28262         });
28263         children.push(style);   
28264         
28265         btn('bold',false,true);
28266         btn('italic',false,true);
28267         btn('align-left', 'justifyleft',true);
28268         btn('align-center', 'justifycenter',true);
28269         btn('align-right' , 'justifyright',true);
28270         btn('link', false, false, function(btn) {
28271             //Roo.log("create link?");
28272             var url = prompt(this.createLinkText, this.defaultLinkValue);
28273             if(url && url != 'http:/'+'/'){
28274                 this.editorcore.relayCmd('createlink', url);
28275             }
28276         }),
28277         btn('list','insertunorderedlist',true);
28278         btn('pencil', false,true, function(btn){
28279                 Roo.log(this);
28280                 this.toggleSourceEdit(btn.pressed);
28281         });
28282         
28283         if (this.editor.btns.length > 0) {
28284             for (var i = 0; i<this.editor.btns.length; i++) {
28285                 children.push(this.editor.btns[i]);
28286             }
28287         }
28288         
28289         /*
28290         var cog = {
28291                 xtype: 'Button',
28292                 size : 'sm',
28293                 xns: Roo.bootstrap,
28294                 glyphicon : 'cog',
28295                 //html : 'submit'
28296                 menu : {
28297                     xtype: 'Menu',
28298                     xns: Roo.bootstrap,
28299                     items:  []
28300                 }
28301         };
28302         
28303         cog.menu.items.push({
28304             xtype :'MenuItem',
28305             xns: Roo.bootstrap,
28306             html : Clean styles,
28307             tagname : f,
28308             listeners : {
28309                 click : function()
28310                 {
28311                     editorcore.insertTag(this.tagname);
28312                     editor.focus();
28313                 }
28314             }
28315             
28316         });
28317        */
28318         
28319          
28320        this.xtype = 'NavSimplebar';
28321         
28322         for(var i=0;i< children.length;i++) {
28323             
28324             this.buttons.add(this.addxtypeChild(children[i]));
28325             
28326         }
28327         
28328         editor.on('editorevent', this.updateToolbar, this);
28329     },
28330     onBtnClick : function(id)
28331     {
28332        this.editorcore.relayCmd(id);
28333        this.editorcore.focus();
28334     },
28335     
28336     /**
28337      * Protected method that will not generally be called directly. It triggers
28338      * a toolbar update by reading the markup state of the current selection in the editor.
28339      */
28340     updateToolbar: function(){
28341
28342         if(!this.editorcore.activated){
28343             this.editor.onFirstFocus(); // is this neeed?
28344             return;
28345         }
28346
28347         var btns = this.buttons; 
28348         var doc = this.editorcore.doc;
28349         btns.get('bold').setActive(doc.queryCommandState('bold'));
28350         btns.get('italic').setActive(doc.queryCommandState('italic'));
28351         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28352         
28353         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28354         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28355         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28356         
28357         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28358         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28359          /*
28360         
28361         var ans = this.editorcore.getAllAncestors();
28362         if (this.formatCombo) {
28363             
28364             
28365             var store = this.formatCombo.store;
28366             this.formatCombo.setValue("");
28367             for (var i =0; i < ans.length;i++) {
28368                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28369                     // select it..
28370                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28371                     break;
28372                 }
28373             }
28374         }
28375         
28376         
28377         
28378         // hides menus... - so this cant be on a menu...
28379         Roo.bootstrap.MenuMgr.hideAll();
28380         */
28381         Roo.bootstrap.menu.Manager.hideAll();
28382         //this.editorsyncValue();
28383     },
28384     onFirstFocus: function() {
28385         this.buttons.each(function(item){
28386            item.enable();
28387         });
28388     },
28389     toggleSourceEdit : function(sourceEditMode){
28390         
28391           
28392         if(sourceEditMode){
28393             Roo.log("disabling buttons");
28394            this.buttons.each( function(item){
28395                 if(item.cmd != 'pencil'){
28396                     item.disable();
28397                 }
28398             });
28399           
28400         }else{
28401             Roo.log("enabling buttons");
28402             if(this.editorcore.initialized){
28403                 this.buttons.each( function(item){
28404                     item.enable();
28405                 });
28406             }
28407             
28408         }
28409         Roo.log("calling toggole on editor");
28410         // tell the editor that it's been pressed..
28411         this.editor.toggleSourceEdit(sourceEditMode);
28412        
28413     }
28414 });
28415
28416
28417
28418
28419  
28420 /*
28421  * - LGPL
28422  */
28423
28424 /**
28425  * @class Roo.bootstrap.form.Markdown
28426  * @extends Roo.bootstrap.form.TextArea
28427  * Bootstrap Showdown editable area
28428  * @cfg {string} content
28429  * 
28430  * @constructor
28431  * Create a new Showdown
28432  */
28433
28434 Roo.bootstrap.form.Markdown = function(config){
28435     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28436    
28437 };
28438
28439 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28440     
28441     editing :false,
28442     
28443     initEvents : function()
28444     {
28445         
28446         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28447         this.markdownEl = this.el.createChild({
28448             cls : 'roo-markdown-area'
28449         });
28450         this.inputEl().addClass('d-none');
28451         if (this.getValue() == '') {
28452             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28453             
28454         } else {
28455             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28456         }
28457         this.markdownEl.on('click', this.toggleTextEdit, this);
28458         this.on('blur', this.toggleTextEdit, this);
28459         this.on('specialkey', this.resizeTextArea, this);
28460     },
28461     
28462     toggleTextEdit : function()
28463     {
28464         var sh = this.markdownEl.getHeight();
28465         this.inputEl().addClass('d-none');
28466         this.markdownEl.addClass('d-none');
28467         if (!this.editing) {
28468             // show editor?
28469             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28470             this.inputEl().removeClass('d-none');
28471             this.inputEl().focus();
28472             this.editing = true;
28473             return;
28474         }
28475         // show showdown...
28476         this.updateMarkdown();
28477         this.markdownEl.removeClass('d-none');
28478         this.editing = false;
28479         return;
28480     },
28481     updateMarkdown : function()
28482     {
28483         if (this.getValue() == '') {
28484             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28485             return;
28486         }
28487  
28488         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28489     },
28490     
28491     resizeTextArea: function () {
28492         
28493         var sh = 100;
28494         Roo.log([sh, this.getValue().split("\n").length * 30]);
28495         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28496     },
28497     setValue : function(val)
28498     {
28499         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28500         if (!this.editing) {
28501             this.updateMarkdown();
28502         }
28503         
28504     },
28505     focus : function()
28506     {
28507         if (!this.editing) {
28508             this.toggleTextEdit();
28509         }
28510         
28511     }
28512
28513
28514 });/*
28515  * Based on:
28516  * Ext JS Library 1.1.1
28517  * Copyright(c) 2006-2007, Ext JS, LLC.
28518  *
28519  * Originally Released Under LGPL - original licence link has changed is not relivant.
28520  *
28521  * Fork - LGPL
28522  * <script type="text/javascript">
28523  */
28524  
28525 /**
28526  * @class Roo.bootstrap.PagingToolbar
28527  * @extends Roo.bootstrap.nav.Simplebar
28528  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28529  * @constructor
28530  * Create a new PagingToolbar
28531  * @param {Object} config The config object
28532  * @param {Roo.data.Store} store
28533  */
28534 Roo.bootstrap.PagingToolbar = function(config)
28535 {
28536     // old args format still supported... - xtype is prefered..
28537         // created from xtype...
28538     
28539     this.ds = config.dataSource;
28540     
28541     if (config.store && !this.ds) {
28542         this.store= Roo.factory(config.store, Roo.data);
28543         this.ds = this.store;
28544         this.ds.xmodule = this.xmodule || false;
28545     }
28546     
28547     this.toolbarItems = [];
28548     if (config.items) {
28549         this.toolbarItems = config.items;
28550     }
28551     
28552     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28553     
28554     this.cursor = 0;
28555     
28556     if (this.ds) { 
28557         this.bind(this.ds);
28558     }
28559     
28560     if (Roo.bootstrap.version == 4) {
28561         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28562     } else {
28563         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28564     }
28565     
28566 };
28567
28568 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28569     /**
28570      * @cfg {Roo.bootstrap.Button} buttons[]
28571      * Buttons for the toolbar
28572      */
28573      /**
28574      * @cfg {Roo.data.Store} store
28575      * The underlying data store providing the paged data
28576      */
28577     /**
28578      * @cfg {String/HTMLElement/Element} container
28579      * container The id or element that will contain the toolbar
28580      */
28581     /**
28582      * @cfg {Boolean} displayInfo
28583      * True to display the displayMsg (defaults to false)
28584      */
28585     /**
28586      * @cfg {Number} pageSize
28587      * The number of records to display per page (defaults to 20)
28588      */
28589     pageSize: 20,
28590     /**
28591      * @cfg {String} displayMsg
28592      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28593      */
28594     displayMsg : 'Displaying {0} - {1} of {2}',
28595     /**
28596      * @cfg {String} emptyMsg
28597      * The message to display when no records are found (defaults to "No data to display")
28598      */
28599     emptyMsg : 'No data to display',
28600     /**
28601      * Customizable piece of the default paging text (defaults to "Page")
28602      * @type String
28603      */
28604     beforePageText : "Page",
28605     /**
28606      * Customizable piece of the default paging text (defaults to "of %0")
28607      * @type String
28608      */
28609     afterPageText : "of {0}",
28610     /**
28611      * Customizable piece of the default paging text (defaults to "First Page")
28612      * @type String
28613      */
28614     firstText : "First Page",
28615     /**
28616      * Customizable piece of the default paging text (defaults to "Previous Page")
28617      * @type String
28618      */
28619     prevText : "Previous Page",
28620     /**
28621      * Customizable piece of the default paging text (defaults to "Next Page")
28622      * @type String
28623      */
28624     nextText : "Next Page",
28625     /**
28626      * Customizable piece of the default paging text (defaults to "Last Page")
28627      * @type String
28628      */
28629     lastText : "Last Page",
28630     /**
28631      * Customizable piece of the default paging text (defaults to "Refresh")
28632      * @type String
28633      */
28634     refreshText : "Refresh",
28635
28636     buttons : false,
28637     // private
28638     onRender : function(ct, position) 
28639     {
28640         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28641         this.navgroup.parentId = this.id;
28642         this.navgroup.onRender(this.el, null);
28643         // add the buttons to the navgroup
28644         
28645         if(this.displayInfo){
28646             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28647             this.displayEl = this.el.select('.x-paging-info', true).first();
28648 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28649 //            this.displayEl = navel.el.select('span',true).first();
28650         }
28651         
28652         var _this = this;
28653         
28654         if(this.buttons){
28655             Roo.each(_this.buttons, function(e){ // this might need to use render????
28656                Roo.factory(e).render(_this.el);
28657             });
28658         }
28659             
28660         Roo.each(_this.toolbarItems, function(e) {
28661             _this.navgroup.addItem(e);
28662         });
28663         
28664         
28665         this.first = this.navgroup.addItem({
28666             tooltip: this.firstText,
28667             cls: "prev btn-outline-secondary",
28668             html : ' <i class="fa fa-step-backward"></i>',
28669             disabled: true,
28670             preventDefault: true,
28671             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28672         });
28673         
28674         this.prev =  this.navgroup.addItem({
28675             tooltip: this.prevText,
28676             cls: "prev btn-outline-secondary",
28677             html : ' <i class="fa fa-backward"></i>',
28678             disabled: true,
28679             preventDefault: true,
28680             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28681         });
28682     //this.addSeparator();
28683         
28684         
28685         var field = this.navgroup.addItem( {
28686             tagtype : 'span',
28687             cls : 'x-paging-position  btn-outline-secondary',
28688              disabled: true,
28689             html : this.beforePageText  +
28690                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28691                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28692          } ); //?? escaped?
28693         
28694         this.field = field.el.select('input', true).first();
28695         this.field.on("keydown", this.onPagingKeydown, this);
28696         this.field.on("focus", function(){this.dom.select();});
28697     
28698     
28699         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28700         //this.field.setHeight(18);
28701         //this.addSeparator();
28702         this.next = this.navgroup.addItem({
28703             tooltip: this.nextText,
28704             cls: "next btn-outline-secondary",
28705             html : ' <i class="fa fa-forward"></i>',
28706             disabled: true,
28707             preventDefault: true,
28708             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28709         });
28710         this.last = this.navgroup.addItem({
28711             tooltip: this.lastText,
28712             html : ' <i class="fa fa-step-forward"></i>',
28713             cls: "next btn-outline-secondary",
28714             disabled: true,
28715             preventDefault: true,
28716             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28717         });
28718     //this.addSeparator();
28719         this.loading = this.navgroup.addItem({
28720             tooltip: this.refreshText,
28721             cls: "btn-outline-secondary",
28722             html : ' <i class="fa fa-refresh"></i>',
28723             preventDefault: true,
28724             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28725         });
28726         
28727     },
28728
28729     // private
28730     updateInfo : function(){
28731         if(this.displayEl){
28732             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28733             var msg = count == 0 ?
28734                 this.emptyMsg :
28735                 String.format(
28736                     this.displayMsg,
28737                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28738                 );
28739             this.displayEl.update(msg);
28740         }
28741     },
28742
28743     // private
28744     onLoad : function(ds, r, o)
28745     {
28746         this.cursor = o.params && o.params.start ? o.params.start : 0;
28747         
28748         var d = this.getPageData(),
28749             ap = d.activePage,
28750             ps = d.pages;
28751         
28752         
28753         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28754         this.field.dom.value = ap;
28755         this.first.setDisabled(ap == 1);
28756         this.prev.setDisabled(ap == 1);
28757         this.next.setDisabled(ap == ps);
28758         this.last.setDisabled(ap == ps);
28759         this.loading.enable();
28760         this.updateInfo();
28761     },
28762
28763     // private
28764     getPageData : function(){
28765         var total = this.ds.getTotalCount();
28766         return {
28767             total : total,
28768             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28769             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28770         };
28771     },
28772
28773     // private
28774     onLoadError : function(){
28775         this.loading.enable();
28776     },
28777
28778     // private
28779     onPagingKeydown : function(e){
28780         var k = e.getKey();
28781         var d = this.getPageData();
28782         if(k == e.RETURN){
28783             var v = this.field.dom.value, pageNum;
28784             if(!v || isNaN(pageNum = parseInt(v, 10))){
28785                 this.field.dom.value = d.activePage;
28786                 return;
28787             }
28788             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28789             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28790             e.stopEvent();
28791         }
28792         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))
28793         {
28794           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28795           this.field.dom.value = pageNum;
28796           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28797           e.stopEvent();
28798         }
28799         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28800         {
28801           var v = this.field.dom.value, pageNum; 
28802           var increment = (e.shiftKey) ? 10 : 1;
28803           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28804                 increment *= -1;
28805           }
28806           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28807             this.field.dom.value = d.activePage;
28808             return;
28809           }
28810           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28811           {
28812             this.field.dom.value = parseInt(v, 10) + increment;
28813             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28814             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28815           }
28816           e.stopEvent();
28817         }
28818     },
28819
28820     // private
28821     beforeLoad : function(){
28822         if(this.loading){
28823             this.loading.disable();
28824         }
28825     },
28826
28827     // private
28828     onClick : function(which){
28829         
28830         var ds = this.ds;
28831         if (!ds) {
28832             return;
28833         }
28834         
28835         switch(which){
28836             case "first":
28837                 ds.load({params:{start: 0, limit: this.pageSize}});
28838             break;
28839             case "prev":
28840                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28841             break;
28842             case "next":
28843                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28844             break;
28845             case "last":
28846                 var total = ds.getTotalCount();
28847                 var extra = total % this.pageSize;
28848                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28849                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28850             break;
28851             case "refresh":
28852                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28853             break;
28854         }
28855     },
28856
28857     /**
28858      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28859      * @param {Roo.data.Store} store The data store to unbind
28860      */
28861     unbind : function(ds){
28862         ds.un("beforeload", this.beforeLoad, this);
28863         ds.un("load", this.onLoad, this);
28864         ds.un("loadexception", this.onLoadError, this);
28865         ds.un("remove", this.updateInfo, this);
28866         ds.un("add", this.updateInfo, this);
28867         this.ds = undefined;
28868     },
28869
28870     /**
28871      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28872      * @param {Roo.data.Store} store The data store to bind
28873      */
28874     bind : function(ds){
28875         ds.on("beforeload", this.beforeLoad, this);
28876         ds.on("load", this.onLoad, this);
28877         ds.on("loadexception", this.onLoadError, this);
28878         ds.on("remove", this.updateInfo, this);
28879         ds.on("add", this.updateInfo, this);
28880         this.ds = ds;
28881     }
28882 });/*
28883  * - LGPL
28884  *
28885  * element
28886  * 
28887  */
28888
28889 /**
28890  * @class Roo.bootstrap.MessageBar
28891  * @extends Roo.bootstrap.Component
28892  * Bootstrap MessageBar class
28893  * @cfg {String} html contents of the MessageBar
28894  * @cfg {String} weight (info | success | warning | danger) default info
28895  * @cfg {String} beforeClass insert the bar before the given class
28896  * @cfg {Boolean} closable (true | false) default false
28897  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28898  * 
28899  * @constructor
28900  * Create a new Element
28901  * @param {Object} config The config object
28902  */
28903
28904 Roo.bootstrap.MessageBar = function(config){
28905     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28906 };
28907
28908 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28909     
28910     html: '',
28911     weight: 'info',
28912     closable: false,
28913     fixed: false,
28914     beforeClass: 'bootstrap-sticky-wrap',
28915     
28916     getAutoCreate : function(){
28917         
28918         var cfg = {
28919             tag: 'div',
28920             cls: 'alert alert-dismissable alert-' + this.weight,
28921             cn: [
28922                 {
28923                     tag: 'span',
28924                     cls: 'message',
28925                     html: this.html || ''
28926                 }
28927             ]
28928         };
28929         
28930         if(this.fixed){
28931             cfg.cls += ' alert-messages-fixed';
28932         }
28933         
28934         if(this.closable){
28935             cfg.cn.push({
28936                 tag: 'button',
28937                 cls: 'close',
28938                 html: 'x'
28939             });
28940         }
28941         
28942         return cfg;
28943     },
28944     
28945     onRender : function(ct, position)
28946     {
28947         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28948         
28949         if(!this.el){
28950             var cfg = Roo.apply({},  this.getAutoCreate());
28951             cfg.id = Roo.id();
28952             
28953             if (this.cls) {
28954                 cfg.cls += ' ' + this.cls;
28955             }
28956             if (this.style) {
28957                 cfg.style = this.style;
28958             }
28959             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28960             
28961             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28962         }
28963         
28964         this.el.select('>button.close').on('click', this.hide, this);
28965         
28966     },
28967     
28968     show : function()
28969     {
28970         if (!this.rendered) {
28971             this.render();
28972         }
28973         
28974         this.el.show();
28975         
28976         this.fireEvent('show', this);
28977         
28978     },
28979     
28980     hide : function()
28981     {
28982         if (!this.rendered) {
28983             this.render();
28984         }
28985         
28986         this.el.hide();
28987         
28988         this.fireEvent('hide', this);
28989     },
28990     
28991     update : function()
28992     {
28993 //        var e = this.el.dom.firstChild;
28994 //        
28995 //        if(this.closable){
28996 //            e = e.nextSibling;
28997 //        }
28998 //        
28999 //        e.data = this.html || '';
29000
29001         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29002     }
29003    
29004 });
29005
29006  
29007
29008      /*
29009  * - LGPL
29010  *
29011  * Graph
29012  * 
29013  */
29014
29015
29016 /**
29017  * @class Roo.bootstrap.Graph
29018  * @extends Roo.bootstrap.Component
29019  * Bootstrap Graph class
29020 > Prameters
29021  -sm {number} sm 4
29022  -md {number} md 5
29023  @cfg {String} graphtype  bar | vbar | pie
29024  @cfg {number} g_x coodinator | centre x (pie)
29025  @cfg {number} g_y coodinator | centre y (pie)
29026  @cfg {number} g_r radius (pie)
29027  @cfg {number} g_height height of the chart (respected by all elements in the set)
29028  @cfg {number} g_width width of the chart (respected by all elements in the set)
29029  @cfg {Object} title The title of the chart
29030     
29031  -{Array}  values
29032  -opts (object) options for the chart 
29033      o {
29034      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29035      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29036      o vgutter (number)
29037      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.
29038      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29039      o to
29040      o stretch (boolean)
29041      o }
29042  -opts (object) options for the pie
29043      o{
29044      o cut
29045      o startAngle (number)
29046      o endAngle (number)
29047      } 
29048  *
29049  * @constructor
29050  * Create a new Input
29051  * @param {Object} config The config object
29052  */
29053
29054 Roo.bootstrap.Graph = function(config){
29055     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29056     
29057     this.addEvents({
29058         // img events
29059         /**
29060          * @event click
29061          * The img click event for the img.
29062          * @param {Roo.EventObject} e
29063          */
29064         "click" : true
29065     });
29066 };
29067
29068 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29069     
29070     sm: 4,
29071     md: 5,
29072     graphtype: 'bar',
29073     g_height: 250,
29074     g_width: 400,
29075     g_x: 50,
29076     g_y: 50,
29077     g_r: 30,
29078     opts:{
29079         //g_colors: this.colors,
29080         g_type: 'soft',
29081         g_gutter: '20%'
29082
29083     },
29084     title : false,
29085
29086     getAutoCreate : function(){
29087         
29088         var cfg = {
29089             tag: 'div',
29090             html : null
29091         };
29092         
29093         
29094         return  cfg;
29095     },
29096
29097     onRender : function(ct,position){
29098         
29099         
29100         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29101         
29102         if (typeof(Raphael) == 'undefined') {
29103             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29104             return;
29105         }
29106         
29107         this.raphael = Raphael(this.el.dom);
29108         
29109                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29110                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29111                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29112                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29113                 /*
29114                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29115                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29116                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29117                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29118                 
29119                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29120                 r.barchart(330, 10, 300, 220, data1);
29121                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29122                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29123                 */
29124                 
29125                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29126                 // r.barchart(30, 30, 560, 250,  xdata, {
29127                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29128                 //     axis : "0 0 1 1",
29129                 //     axisxlabels :  xdata
29130                 //     //yvalues : cols,
29131                    
29132                 // });
29133 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29134 //        
29135 //        this.load(null,xdata,{
29136 //                axis : "0 0 1 1",
29137 //                axisxlabels :  xdata
29138 //                });
29139
29140     },
29141
29142     load : function(graphtype,xdata,opts)
29143     {
29144         this.raphael.clear();
29145         if(!graphtype) {
29146             graphtype = this.graphtype;
29147         }
29148         if(!opts){
29149             opts = this.opts;
29150         }
29151         var r = this.raphael,
29152             fin = function () {
29153                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29154             },
29155             fout = function () {
29156                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29157             },
29158             pfin = function() {
29159                 this.sector.stop();
29160                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29161
29162                 if (this.label) {
29163                     this.label[0].stop();
29164                     this.label[0].attr({ r: 7.5 });
29165                     this.label[1].attr({ "font-weight": 800 });
29166                 }
29167             },
29168             pfout = function() {
29169                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29170
29171                 if (this.label) {
29172                     this.label[0].animate({ r: 5 }, 500, "bounce");
29173                     this.label[1].attr({ "font-weight": 400 });
29174                 }
29175             };
29176
29177         switch(graphtype){
29178             case 'bar':
29179                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29180                 break;
29181             case 'hbar':
29182                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29183                 break;
29184             case 'pie':
29185 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29186 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29187 //            
29188                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29189                 
29190                 break;
29191
29192         }
29193         
29194         if(this.title){
29195             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29196         }
29197         
29198     },
29199     
29200     setTitle: function(o)
29201     {
29202         this.title = o;
29203     },
29204     
29205     initEvents: function() {
29206         
29207         if(!this.href){
29208             this.el.on('click', this.onClick, this);
29209         }
29210     },
29211     
29212     onClick : function(e)
29213     {
29214         Roo.log('img onclick');
29215         this.fireEvent('click', this, e);
29216     }
29217    
29218 });
29219
29220  
29221 /*
29222  * - LGPL
29223  *
29224  * numberBox
29225  * 
29226  */
29227 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29228
29229 /**
29230  * @class Roo.bootstrap.dash.NumberBox
29231  * @extends Roo.bootstrap.Component
29232  * Bootstrap NumberBox class
29233  * @cfg {String} headline Box headline
29234  * @cfg {String} content Box content
29235  * @cfg {String} icon Box icon
29236  * @cfg {String} footer Footer text
29237  * @cfg {String} fhref Footer href
29238  * 
29239  * @constructor
29240  * Create a new NumberBox
29241  * @param {Object} config The config object
29242  */
29243
29244
29245 Roo.bootstrap.dash.NumberBox = function(config){
29246     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29247     
29248 };
29249
29250 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29251     
29252     headline : '',
29253     content : '',
29254     icon : '',
29255     footer : '',
29256     fhref : '',
29257     ficon : '',
29258     
29259     getAutoCreate : function(){
29260         
29261         var cfg = {
29262             tag : 'div',
29263             cls : 'small-box ',
29264             cn : [
29265                 {
29266                     tag : 'div',
29267                     cls : 'inner',
29268                     cn :[
29269                         {
29270                             tag : 'h3',
29271                             cls : 'roo-headline',
29272                             html : this.headline
29273                         },
29274                         {
29275                             tag : 'p',
29276                             cls : 'roo-content',
29277                             html : this.content
29278                         }
29279                     ]
29280                 }
29281             ]
29282         };
29283         
29284         if(this.icon){
29285             cfg.cn.push({
29286                 tag : 'div',
29287                 cls : 'icon',
29288                 cn :[
29289                     {
29290                         tag : 'i',
29291                         cls : 'ion ' + this.icon
29292                     }
29293                 ]
29294             });
29295         }
29296         
29297         if(this.footer){
29298             var footer = {
29299                 tag : 'a',
29300                 cls : 'small-box-footer',
29301                 href : this.fhref || '#',
29302                 html : this.footer
29303             };
29304             
29305             cfg.cn.push(footer);
29306             
29307         }
29308         
29309         return  cfg;
29310     },
29311
29312     onRender : function(ct,position){
29313         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29314
29315
29316        
29317                 
29318     },
29319
29320     setHeadline: function (value)
29321     {
29322         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29323     },
29324     
29325     setFooter: function (value, href)
29326     {
29327         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29328         
29329         if(href){
29330             this.el.select('a.small-box-footer',true).first().attr('href', href);
29331         }
29332         
29333     },
29334
29335     setContent: function (value)
29336     {
29337         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29338     },
29339
29340     initEvents: function() 
29341     {   
29342         
29343     }
29344     
29345 });
29346
29347  
29348 /*
29349  * - LGPL
29350  *
29351  * TabBox
29352  * 
29353  */
29354 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29355
29356 /**
29357  * @class Roo.bootstrap.dash.TabBox
29358  * @extends Roo.bootstrap.Component
29359  * @children Roo.bootstrap.dash.TabPane
29360  * Bootstrap TabBox class
29361  * @cfg {String} title Title of the TabBox
29362  * @cfg {String} icon Icon of the TabBox
29363  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29364  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29365  * 
29366  * @constructor
29367  * Create a new TabBox
29368  * @param {Object} config The config object
29369  */
29370
29371
29372 Roo.bootstrap.dash.TabBox = function(config){
29373     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29374     this.addEvents({
29375         // raw events
29376         /**
29377          * @event addpane
29378          * When a pane is added
29379          * @param {Roo.bootstrap.dash.TabPane} pane
29380          */
29381         "addpane" : true,
29382         /**
29383          * @event activatepane
29384          * When a pane is activated
29385          * @param {Roo.bootstrap.dash.TabPane} pane
29386          */
29387         "activatepane" : true
29388         
29389          
29390     });
29391     
29392     this.panes = [];
29393 };
29394
29395 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29396
29397     title : '',
29398     icon : false,
29399     showtabs : true,
29400     tabScrollable : false,
29401     
29402     getChildContainer : function()
29403     {
29404         return this.el.select('.tab-content', true).first();
29405     },
29406     
29407     getAutoCreate : function(){
29408         
29409         var header = {
29410             tag: 'li',
29411             cls: 'pull-left header',
29412             html: this.title,
29413             cn : []
29414         };
29415         
29416         if(this.icon){
29417             header.cn.push({
29418                 tag: 'i',
29419                 cls: 'fa ' + this.icon
29420             });
29421         }
29422         
29423         var h = {
29424             tag: 'ul',
29425             cls: 'nav nav-tabs pull-right',
29426             cn: [
29427                 header
29428             ]
29429         };
29430         
29431         if(this.tabScrollable){
29432             h = {
29433                 tag: 'div',
29434                 cls: 'tab-header',
29435                 cn: [
29436                     {
29437                         tag: 'ul',
29438                         cls: 'nav nav-tabs pull-right',
29439                         cn: [
29440                             header
29441                         ]
29442                     }
29443                 ]
29444             };
29445         }
29446         
29447         var cfg = {
29448             tag: 'div',
29449             cls: 'nav-tabs-custom',
29450             cn: [
29451                 h,
29452                 {
29453                     tag: 'div',
29454                     cls: 'tab-content no-padding',
29455                     cn: []
29456                 }
29457             ]
29458         };
29459
29460         return  cfg;
29461     },
29462     initEvents : function()
29463     {
29464         //Roo.log('add add pane handler');
29465         this.on('addpane', this.onAddPane, this);
29466     },
29467      /**
29468      * Updates the box title
29469      * @param {String} html to set the title to.
29470      */
29471     setTitle : function(value)
29472     {
29473         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29474     },
29475     onAddPane : function(pane)
29476     {
29477         this.panes.push(pane);
29478         //Roo.log('addpane');
29479         //Roo.log(pane);
29480         // tabs are rendere left to right..
29481         if(!this.showtabs){
29482             return;
29483         }
29484         
29485         var ctr = this.el.select('.nav-tabs', true).first();
29486          
29487          
29488         var existing = ctr.select('.nav-tab',true);
29489         var qty = existing.getCount();;
29490         
29491         
29492         var tab = ctr.createChild({
29493             tag : 'li',
29494             cls : 'nav-tab' + (qty ? '' : ' active'),
29495             cn : [
29496                 {
29497                     tag : 'a',
29498                     href:'#',
29499                     html : pane.title
29500                 }
29501             ]
29502         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29503         pane.tab = tab;
29504         
29505         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29506         if (!qty) {
29507             pane.el.addClass('active');
29508         }
29509         
29510                 
29511     },
29512     onTabClick : function(ev,un,ob,pane)
29513     {
29514         //Roo.log('tab - prev default');
29515         ev.preventDefault();
29516         
29517         
29518         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29519         pane.tab.addClass('active');
29520         //Roo.log(pane.title);
29521         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29522         // technically we should have a deactivate event.. but maybe add later.
29523         // and it should not de-activate the selected tab...
29524         this.fireEvent('activatepane', pane);
29525         pane.el.addClass('active');
29526         pane.fireEvent('activate');
29527         
29528         
29529     },
29530     
29531     getActivePane : function()
29532     {
29533         var r = false;
29534         Roo.each(this.panes, function(p) {
29535             if(p.el.hasClass('active')){
29536                 r = p;
29537                 return false;
29538             }
29539             
29540             return;
29541         });
29542         
29543         return r;
29544     }
29545     
29546     
29547 });
29548
29549  
29550 /*
29551  * - LGPL
29552  *
29553  * Tab pane
29554  * 
29555  */
29556 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29557 /**
29558  * @class Roo.bootstrap.TabPane
29559  * @extends Roo.bootstrap.Component
29560  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29561  * Bootstrap TabPane class
29562  * @cfg {Boolean} active (false | true) Default false
29563  * @cfg {String} title title of panel
29564
29565  * 
29566  * @constructor
29567  * Create a new TabPane
29568  * @param {Object} config The config object
29569  */
29570
29571 Roo.bootstrap.dash.TabPane = function(config){
29572     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29573     
29574     this.addEvents({
29575         // raw events
29576         /**
29577          * @event activate
29578          * When a pane is activated
29579          * @param {Roo.bootstrap.dash.TabPane} pane
29580          */
29581         "activate" : true
29582          
29583     });
29584 };
29585
29586 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29587     
29588     active : false,
29589     title : '',
29590     
29591     // the tabBox that this is attached to.
29592     tab : false,
29593      
29594     getAutoCreate : function() 
29595     {
29596         var cfg = {
29597             tag: 'div',
29598             cls: 'tab-pane'
29599         };
29600         
29601         if(this.active){
29602             cfg.cls += ' active';
29603         }
29604         
29605         return cfg;
29606     },
29607     initEvents  : function()
29608     {
29609         //Roo.log('trigger add pane handler');
29610         this.parent().fireEvent('addpane', this)
29611     },
29612     
29613      /**
29614      * Updates the tab title 
29615      * @param {String} html to set the title to.
29616      */
29617     setTitle: function(str)
29618     {
29619         if (!this.tab) {
29620             return;
29621         }
29622         this.title = str;
29623         this.tab.select('a', true).first().dom.innerHTML = str;
29624         
29625     }
29626     
29627     
29628     
29629 });
29630
29631  
29632
29633
29634  /*
29635  * - LGPL
29636  *
29637  * Tooltip
29638  * 
29639  */
29640
29641 /**
29642  * @class Roo.bootstrap.Tooltip
29643  * Bootstrap Tooltip class
29644  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29645  * to determine which dom element triggers the tooltip.
29646  * 
29647  * It needs to add support for additional attributes like tooltip-position
29648  * 
29649  * @constructor
29650  * Create a new Toolti
29651  * @param {Object} config The config object
29652  */
29653
29654 Roo.bootstrap.Tooltip = function(config){
29655     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29656     
29657     this.alignment = Roo.bootstrap.Tooltip.alignment;
29658     
29659     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29660         this.alignment = config.alignment;
29661     }
29662     
29663 };
29664
29665 Roo.apply(Roo.bootstrap.Tooltip, {
29666     /**
29667      * @function init initialize tooltip monitoring.
29668      * @static
29669      */
29670     currentEl : false,
29671     currentTip : false,
29672     currentRegion : false,
29673     
29674     //  init : delay?
29675     
29676     init : function()
29677     {
29678         Roo.get(document).on('mouseover', this.enter ,this);
29679         Roo.get(document).on('mouseout', this.leave, this);
29680          
29681         
29682         this.currentTip = new Roo.bootstrap.Tooltip();
29683     },
29684     
29685     enter : function(ev)
29686     {
29687         var dom = ev.getTarget();
29688         
29689         //Roo.log(['enter',dom]);
29690         var el = Roo.fly(dom);
29691         if (this.currentEl) {
29692             //Roo.log(dom);
29693             //Roo.log(this.currentEl);
29694             //Roo.log(this.currentEl.contains(dom));
29695             if (this.currentEl == el) {
29696                 return;
29697             }
29698             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29699                 return;
29700             }
29701
29702         }
29703         
29704         if (this.currentTip.el) {
29705             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29706         }    
29707         //Roo.log(ev);
29708         
29709         if(!el || el.dom == document){
29710             return;
29711         }
29712         
29713         var bindEl = el; 
29714         var pel = false;
29715         if (!el.attr('tooltip')) {
29716             pel = el.findParent("[tooltip]");
29717             if (pel) {
29718                 bindEl = Roo.get(pel);
29719             }
29720         }
29721         
29722        
29723         
29724         // you can not look for children, as if el is the body.. then everythign is the child..
29725         if (!pel && !el.attr('tooltip')) { //
29726             if (!el.select("[tooltip]").elements.length) {
29727                 return;
29728             }
29729             // is the mouse over this child...?
29730             bindEl = el.select("[tooltip]").first();
29731             var xy = ev.getXY();
29732             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29733                 //Roo.log("not in region.");
29734                 return;
29735             }
29736             //Roo.log("child element over..");
29737             
29738         }
29739         this.currentEl = el;
29740         this.currentTip.bind(bindEl);
29741         this.currentRegion = Roo.lib.Region.getRegion(dom);
29742         this.currentTip.enter();
29743         
29744     },
29745     leave : function(ev)
29746     {
29747         var dom = ev.getTarget();
29748         //Roo.log(['leave',dom]);
29749         if (!this.currentEl) {
29750             return;
29751         }
29752         
29753         
29754         if (dom != this.currentEl.dom) {
29755             return;
29756         }
29757         var xy = ev.getXY();
29758         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29759             return;
29760         }
29761         // only activate leave if mouse cursor is outside... bounding box..
29762         
29763         
29764         
29765         
29766         if (this.currentTip) {
29767             this.currentTip.leave();
29768         }
29769         //Roo.log('clear currentEl');
29770         this.currentEl = false;
29771         
29772         
29773     },
29774     alignment : {
29775         'left' : ['r-l', [-2,0], 'right'],
29776         'right' : ['l-r', [2,0], 'left'],
29777         'bottom' : ['t-b', [0,2], 'top'],
29778         'top' : [ 'b-t', [0,-2], 'bottom']
29779     }
29780     
29781 });
29782
29783
29784 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29785     
29786     
29787     bindEl : false,
29788     
29789     delay : null, // can be { show : 300 , hide: 500}
29790     
29791     timeout : null,
29792     
29793     hoverState : null, //???
29794     
29795     placement : 'bottom', 
29796     
29797     alignment : false,
29798     
29799     getAutoCreate : function(){
29800     
29801         var cfg = {
29802            cls : 'tooltip',   
29803            role : 'tooltip',
29804            cn : [
29805                 {
29806                     cls : 'tooltip-arrow arrow'
29807                 },
29808                 {
29809                     cls : 'tooltip-inner'
29810                 }
29811            ]
29812         };
29813         
29814         return cfg;
29815     },
29816     bind : function(el)
29817     {
29818         this.bindEl = el;
29819     },
29820     
29821     initEvents : function()
29822     {
29823         this.arrowEl = this.el.select('.arrow', true).first();
29824         this.innerEl = this.el.select('.tooltip-inner', true).first();
29825     },
29826     
29827     enter : function () {
29828        
29829         if (this.timeout != null) {
29830             clearTimeout(this.timeout);
29831         }
29832         
29833         this.hoverState = 'in';
29834          //Roo.log("enter - show");
29835         if (!this.delay || !this.delay.show) {
29836             this.show();
29837             return;
29838         }
29839         var _t = this;
29840         this.timeout = setTimeout(function () {
29841             if (_t.hoverState == 'in') {
29842                 _t.show();
29843             }
29844         }, this.delay.show);
29845     },
29846     leave : function()
29847     {
29848         clearTimeout(this.timeout);
29849     
29850         this.hoverState = 'out';
29851          if (!this.delay || !this.delay.hide) {
29852             this.hide();
29853             return;
29854         }
29855        
29856         var _t = this;
29857         this.timeout = setTimeout(function () {
29858             //Roo.log("leave - timeout");
29859             
29860             if (_t.hoverState == 'out') {
29861                 _t.hide();
29862                 Roo.bootstrap.Tooltip.currentEl = false;
29863             }
29864         }, delay);
29865     },
29866     
29867     show : function (msg)
29868     {
29869         if (!this.el) {
29870             this.render(document.body);
29871         }
29872         // set content.
29873         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29874         
29875         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29876         
29877         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29878         
29879         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29880                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29881         
29882         var placement = typeof this.placement == 'function' ?
29883             this.placement.call(this, this.el, on_el) :
29884             this.placement;
29885             
29886         var autoToken = /\s?auto?\s?/i;
29887         var autoPlace = autoToken.test(placement);
29888         if (autoPlace) {
29889             placement = placement.replace(autoToken, '') || 'top';
29890         }
29891         
29892         //this.el.detach()
29893         //this.el.setXY([0,0]);
29894         this.el.show();
29895         //this.el.dom.style.display='block';
29896         
29897         //this.el.appendTo(on_el);
29898         
29899         var p = this.getPosition();
29900         var box = this.el.getBox();
29901         
29902         if (autoPlace) {
29903             // fixme..
29904         }
29905         
29906         var align = this.alignment[placement];
29907         
29908         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29909         
29910         if(placement == 'top' || placement == 'bottom'){
29911             if(xy[0] < 0){
29912                 placement = 'right';
29913             }
29914             
29915             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29916                 placement = 'left';
29917             }
29918             
29919             var scroll = Roo.select('body', true).first().getScroll();
29920             
29921             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29922                 placement = 'top';
29923             }
29924             
29925             align = this.alignment[placement];
29926             
29927             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29928             
29929         }
29930         
29931         var elems = document.getElementsByTagName('div');
29932         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29933         for (var i = 0; i < elems.length; i++) {
29934           var zindex = Number.parseInt(
29935                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29936                 10
29937           );
29938           if (zindex > highest) {
29939             highest = zindex;
29940           }
29941         }
29942         
29943         
29944         
29945         this.el.dom.style.zIndex = highest;
29946         
29947         this.el.alignTo(this.bindEl, align[0],align[1]);
29948         //var arrow = this.el.select('.arrow',true).first();
29949         //arrow.set(align[2], 
29950         
29951         this.el.addClass(placement);
29952         this.el.addClass("bs-tooltip-"+ placement);
29953         
29954         this.el.addClass('in fade show');
29955         
29956         this.hoverState = null;
29957         
29958         if (this.el.hasClass('fade')) {
29959             // fade it?
29960         }
29961         
29962         
29963         
29964         
29965         
29966     },
29967     hide : function()
29968     {
29969          
29970         if (!this.el) {
29971             return;
29972         }
29973         //this.el.setXY([0,0]);
29974         this.el.removeClass(['show', 'in']);
29975         //this.el.hide();
29976         
29977     }
29978     
29979 });
29980  
29981
29982  /*
29983  * - LGPL
29984  *
29985  * Location Picker
29986  * 
29987  */
29988
29989 /**
29990  * @class Roo.bootstrap.LocationPicker
29991  * @extends Roo.bootstrap.Component
29992  * Bootstrap LocationPicker class
29993  * @cfg {Number} latitude Position when init default 0
29994  * @cfg {Number} longitude Position when init default 0
29995  * @cfg {Number} zoom default 15
29996  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29997  * @cfg {Boolean} mapTypeControl default false
29998  * @cfg {Boolean} disableDoubleClickZoom default false
29999  * @cfg {Boolean} scrollwheel default true
30000  * @cfg {Boolean} streetViewControl default false
30001  * @cfg {Number} radius default 0
30002  * @cfg {String} locationName
30003  * @cfg {Boolean} draggable default true
30004  * @cfg {Boolean} enableAutocomplete default false
30005  * @cfg {Boolean} enableReverseGeocode default true
30006  * @cfg {String} markerTitle
30007  * 
30008  * @constructor
30009  * Create a new LocationPicker
30010  * @param {Object} config The config object
30011  */
30012
30013
30014 Roo.bootstrap.LocationPicker = function(config){
30015     
30016     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30017     
30018     this.addEvents({
30019         /**
30020          * @event initial
30021          * Fires when the picker initialized.
30022          * @param {Roo.bootstrap.LocationPicker} this
30023          * @param {Google Location} location
30024          */
30025         initial : true,
30026         /**
30027          * @event positionchanged
30028          * Fires when the picker position changed.
30029          * @param {Roo.bootstrap.LocationPicker} this
30030          * @param {Google Location} location
30031          */
30032         positionchanged : true,
30033         /**
30034          * @event resize
30035          * Fires when the map resize.
30036          * @param {Roo.bootstrap.LocationPicker} this
30037          */
30038         resize : true,
30039         /**
30040          * @event show
30041          * Fires when the map show.
30042          * @param {Roo.bootstrap.LocationPicker} this
30043          */
30044         show : true,
30045         /**
30046          * @event hide
30047          * Fires when the map hide.
30048          * @param {Roo.bootstrap.LocationPicker} this
30049          */
30050         hide : true,
30051         /**
30052          * @event mapClick
30053          * Fires when click the map.
30054          * @param {Roo.bootstrap.LocationPicker} this
30055          * @param {Map event} e
30056          */
30057         mapClick : true,
30058         /**
30059          * @event mapRightClick
30060          * Fires when right click the map.
30061          * @param {Roo.bootstrap.LocationPicker} this
30062          * @param {Map event} e
30063          */
30064         mapRightClick : true,
30065         /**
30066          * @event markerClick
30067          * Fires when click the marker.
30068          * @param {Roo.bootstrap.LocationPicker} this
30069          * @param {Map event} e
30070          */
30071         markerClick : true,
30072         /**
30073          * @event markerRightClick
30074          * Fires when right click the marker.
30075          * @param {Roo.bootstrap.LocationPicker} this
30076          * @param {Map event} e
30077          */
30078         markerRightClick : true,
30079         /**
30080          * @event OverlayViewDraw
30081          * Fires when OverlayView Draw
30082          * @param {Roo.bootstrap.LocationPicker} this
30083          */
30084         OverlayViewDraw : true,
30085         /**
30086          * @event OverlayViewOnAdd
30087          * Fires when OverlayView Draw
30088          * @param {Roo.bootstrap.LocationPicker} this
30089          */
30090         OverlayViewOnAdd : true,
30091         /**
30092          * @event OverlayViewOnRemove
30093          * Fires when OverlayView Draw
30094          * @param {Roo.bootstrap.LocationPicker} this
30095          */
30096         OverlayViewOnRemove : true,
30097         /**
30098          * @event OverlayViewShow
30099          * Fires when OverlayView Draw
30100          * @param {Roo.bootstrap.LocationPicker} this
30101          * @param {Pixel} cpx
30102          */
30103         OverlayViewShow : true,
30104         /**
30105          * @event OverlayViewHide
30106          * Fires when OverlayView Draw
30107          * @param {Roo.bootstrap.LocationPicker} this
30108          */
30109         OverlayViewHide : true,
30110         /**
30111          * @event loadexception
30112          * Fires when load google lib failed.
30113          * @param {Roo.bootstrap.LocationPicker} this
30114          */
30115         loadexception : true
30116     });
30117         
30118 };
30119
30120 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30121     
30122     gMapContext: false,
30123     
30124     latitude: 0,
30125     longitude: 0,
30126     zoom: 15,
30127     mapTypeId: false,
30128     mapTypeControl: false,
30129     disableDoubleClickZoom: false,
30130     scrollwheel: true,
30131     streetViewControl: false,
30132     radius: 0,
30133     locationName: '',
30134     draggable: true,
30135     enableAutocomplete: false,
30136     enableReverseGeocode: true,
30137     markerTitle: '',
30138     
30139     getAutoCreate: function()
30140     {
30141
30142         var cfg = {
30143             tag: 'div',
30144             cls: 'roo-location-picker'
30145         };
30146         
30147         return cfg
30148     },
30149     
30150     initEvents: function(ct, position)
30151     {       
30152         if(!this.el.getWidth() || this.isApplied()){
30153             return;
30154         }
30155         
30156         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30157         
30158         this.initial();
30159     },
30160     
30161     initial: function()
30162     {
30163         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30164             this.fireEvent('loadexception', this);
30165             return;
30166         }
30167         
30168         if(!this.mapTypeId){
30169             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30170         }
30171         
30172         this.gMapContext = this.GMapContext();
30173         
30174         this.initOverlayView();
30175         
30176         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30177         
30178         var _this = this;
30179                 
30180         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30181             _this.setPosition(_this.gMapContext.marker.position);
30182         });
30183         
30184         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30185             _this.fireEvent('mapClick', this, event);
30186             
30187         });
30188
30189         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30190             _this.fireEvent('mapRightClick', this, event);
30191             
30192         });
30193         
30194         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30195             _this.fireEvent('markerClick', this, event);
30196             
30197         });
30198
30199         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30200             _this.fireEvent('markerRightClick', this, event);
30201             
30202         });
30203         
30204         this.setPosition(this.gMapContext.location);
30205         
30206         this.fireEvent('initial', this, this.gMapContext.location);
30207     },
30208     
30209     initOverlayView: function()
30210     {
30211         var _this = this;
30212         
30213         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30214             
30215             draw: function()
30216             {
30217                 _this.fireEvent('OverlayViewDraw', _this);
30218             },
30219             
30220             onAdd: function()
30221             {
30222                 _this.fireEvent('OverlayViewOnAdd', _this);
30223             },
30224             
30225             onRemove: function()
30226             {
30227                 _this.fireEvent('OverlayViewOnRemove', _this);
30228             },
30229             
30230             show: function(cpx)
30231             {
30232                 _this.fireEvent('OverlayViewShow', _this, cpx);
30233             },
30234             
30235             hide: function()
30236             {
30237                 _this.fireEvent('OverlayViewHide', _this);
30238             }
30239             
30240         });
30241     },
30242     
30243     fromLatLngToContainerPixel: function(event)
30244     {
30245         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30246     },
30247     
30248     isApplied: function() 
30249     {
30250         return this.getGmapContext() == false ? false : true;
30251     },
30252     
30253     getGmapContext: function() 
30254     {
30255         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30256     },
30257     
30258     GMapContext: function() 
30259     {
30260         var position = new google.maps.LatLng(this.latitude, this.longitude);
30261         
30262         var _map = new google.maps.Map(this.el.dom, {
30263             center: position,
30264             zoom: this.zoom,
30265             mapTypeId: this.mapTypeId,
30266             mapTypeControl: this.mapTypeControl,
30267             disableDoubleClickZoom: this.disableDoubleClickZoom,
30268             scrollwheel: this.scrollwheel,
30269             streetViewControl: this.streetViewControl,
30270             locationName: this.locationName,
30271             draggable: this.draggable,
30272             enableAutocomplete: this.enableAutocomplete,
30273             enableReverseGeocode: this.enableReverseGeocode
30274         });
30275         
30276         var _marker = new google.maps.Marker({
30277             position: position,
30278             map: _map,
30279             title: this.markerTitle,
30280             draggable: this.draggable
30281         });
30282         
30283         return {
30284             map: _map,
30285             marker: _marker,
30286             circle: null,
30287             location: position,
30288             radius: this.radius,
30289             locationName: this.locationName,
30290             addressComponents: {
30291                 formatted_address: null,
30292                 addressLine1: null,
30293                 addressLine2: null,
30294                 streetName: null,
30295                 streetNumber: null,
30296                 city: null,
30297                 district: null,
30298                 state: null,
30299                 stateOrProvince: null
30300             },
30301             settings: this,
30302             domContainer: this.el.dom,
30303             geodecoder: new google.maps.Geocoder()
30304         };
30305     },
30306     
30307     drawCircle: function(center, radius, options) 
30308     {
30309         if (this.gMapContext.circle != null) {
30310             this.gMapContext.circle.setMap(null);
30311         }
30312         if (radius > 0) {
30313             radius *= 1;
30314             options = Roo.apply({}, options, {
30315                 strokeColor: "#0000FF",
30316                 strokeOpacity: .35,
30317                 strokeWeight: 2,
30318                 fillColor: "#0000FF",
30319                 fillOpacity: .2
30320             });
30321             
30322             options.map = this.gMapContext.map;
30323             options.radius = radius;
30324             options.center = center;
30325             this.gMapContext.circle = new google.maps.Circle(options);
30326             return this.gMapContext.circle;
30327         }
30328         
30329         return null;
30330     },
30331     
30332     setPosition: function(location) 
30333     {
30334         this.gMapContext.location = location;
30335         this.gMapContext.marker.setPosition(location);
30336         this.gMapContext.map.panTo(location);
30337         this.drawCircle(location, this.gMapContext.radius, {});
30338         
30339         var _this = this;
30340         
30341         if (this.gMapContext.settings.enableReverseGeocode) {
30342             this.gMapContext.geodecoder.geocode({
30343                 latLng: this.gMapContext.location
30344             }, function(results, status) {
30345                 
30346                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30347                     _this.gMapContext.locationName = results[0].formatted_address;
30348                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30349                     
30350                     _this.fireEvent('positionchanged', this, location);
30351                 }
30352             });
30353             
30354             return;
30355         }
30356         
30357         this.fireEvent('positionchanged', this, location);
30358     },
30359     
30360     resize: function()
30361     {
30362         google.maps.event.trigger(this.gMapContext.map, "resize");
30363         
30364         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30365         
30366         this.fireEvent('resize', this);
30367     },
30368     
30369     setPositionByLatLng: function(latitude, longitude)
30370     {
30371         this.setPosition(new google.maps.LatLng(latitude, longitude));
30372     },
30373     
30374     getCurrentPosition: function() 
30375     {
30376         return {
30377             latitude: this.gMapContext.location.lat(),
30378             longitude: this.gMapContext.location.lng()
30379         };
30380     },
30381     
30382     getAddressName: function() 
30383     {
30384         return this.gMapContext.locationName;
30385     },
30386     
30387     getAddressComponents: function() 
30388     {
30389         return this.gMapContext.addressComponents;
30390     },
30391     
30392     address_component_from_google_geocode: function(address_components) 
30393     {
30394         var result = {};
30395         
30396         for (var i = 0; i < address_components.length; i++) {
30397             var component = address_components[i];
30398             if (component.types.indexOf("postal_code") >= 0) {
30399                 result.postalCode = component.short_name;
30400             } else if (component.types.indexOf("street_number") >= 0) {
30401                 result.streetNumber = component.short_name;
30402             } else if (component.types.indexOf("route") >= 0) {
30403                 result.streetName = component.short_name;
30404             } else if (component.types.indexOf("neighborhood") >= 0) {
30405                 result.city = component.short_name;
30406             } else if (component.types.indexOf("locality") >= 0) {
30407                 result.city = component.short_name;
30408             } else if (component.types.indexOf("sublocality") >= 0) {
30409                 result.district = component.short_name;
30410             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30411                 result.stateOrProvince = component.short_name;
30412             } else if (component.types.indexOf("country") >= 0) {
30413                 result.country = component.short_name;
30414             }
30415         }
30416         
30417         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30418         result.addressLine2 = "";
30419         return result;
30420     },
30421     
30422     setZoomLevel: function(zoom)
30423     {
30424         this.gMapContext.map.setZoom(zoom);
30425     },
30426     
30427     show: function()
30428     {
30429         if(!this.el){
30430             return;
30431         }
30432         
30433         this.el.show();
30434         
30435         this.resize();
30436         
30437         this.fireEvent('show', this);
30438     },
30439     
30440     hide: function()
30441     {
30442         if(!this.el){
30443             return;
30444         }
30445         
30446         this.el.hide();
30447         
30448         this.fireEvent('hide', this);
30449     }
30450     
30451 });
30452
30453 Roo.apply(Roo.bootstrap.LocationPicker, {
30454     
30455     OverlayView : function(map, options)
30456     {
30457         options = options || {};
30458         
30459         this.setMap(map);
30460     }
30461     
30462     
30463 });/**
30464  * @class Roo.bootstrap.Alert
30465  * @extends Roo.bootstrap.Component
30466  * Bootstrap Alert class - shows an alert area box
30467  * eg
30468  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30469   Enter a valid email address
30470 </div>
30471  * @licence LGPL
30472  * @cfg {String} title The title of alert
30473  * @cfg {String} html The content of alert
30474  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30475  * @cfg {String} fa font-awesomeicon
30476  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30477  * @cfg {Boolean} close true to show a x closer
30478  * 
30479  * 
30480  * @constructor
30481  * Create a new alert
30482  * @param {Object} config The config object
30483  */
30484
30485
30486 Roo.bootstrap.Alert = function(config){
30487     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30488     
30489 };
30490
30491 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30492     
30493     title: '',
30494     html: '',
30495     weight: false,
30496     fa: false,
30497     faicon: false, // BC
30498     close : false,
30499     
30500     
30501     getAutoCreate : function()
30502     {
30503         
30504         var cfg = {
30505             tag : 'div',
30506             cls : 'alert',
30507             cn : [
30508                 {
30509                     tag: 'button',
30510                     type :  "button",
30511                     cls: "close",
30512                     html : '×',
30513                     style : this.close ? '' : 'display:none'
30514                 },
30515                 {
30516                     tag : 'i',
30517                     cls : 'roo-alert-icon'
30518                     
30519                 },
30520                 {
30521                     tag : 'b',
30522                     cls : 'roo-alert-title',
30523                     html : this.title
30524                 },
30525                 {
30526                     tag : 'span',
30527                     cls : 'roo-alert-text',
30528                     html : this.html
30529                 }
30530             ]
30531         };
30532         
30533         if(this.faicon){
30534             cfg.cn[0].cls += ' fa ' + this.faicon;
30535         }
30536         if(this.fa){
30537             cfg.cn[0].cls += ' fa ' + this.fa;
30538         }
30539         
30540         if(this.weight){
30541             cfg.cls += ' alert-' + this.weight;
30542         }
30543         
30544         return cfg;
30545     },
30546     
30547     initEvents: function() 
30548     {
30549         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30550         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30551         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30552         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30553         if (this.seconds > 0) {
30554             this.hide.defer(this.seconds, this);
30555         }
30556     },
30557     /**
30558      * Set the Title Message HTML
30559      * @param {String} html
30560      */
30561     setTitle : function(str)
30562     {
30563         this.titleEl.dom.innerHTML = str;
30564     },
30565      
30566      /**
30567      * Set the Body Message HTML
30568      * @param {String} html
30569      */
30570     setHtml : function(str)
30571     {
30572         this.htmlEl.dom.innerHTML = str;
30573     },
30574     /**
30575      * Set the Weight of the alert
30576      * @param {String} (success|info|warning|danger) weight
30577      */
30578     
30579     setWeight : function(weight)
30580     {
30581         if(this.weight){
30582             this.el.removeClass('alert-' + this.weight);
30583         }
30584         
30585         this.weight = weight;
30586         
30587         this.el.addClass('alert-' + this.weight);
30588     },
30589       /**
30590      * Set the Icon of the alert
30591      * @param {String} see fontawsome names (name without the 'fa-' bit)
30592      */
30593     setIcon : function(icon)
30594     {
30595         if(this.faicon){
30596             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30597         }
30598         
30599         this.faicon = icon;
30600         
30601         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30602     },
30603     /**
30604      * Hide the Alert
30605      */
30606     hide: function() 
30607     {
30608         this.el.hide();   
30609     },
30610     /**
30611      * Show the Alert
30612      */
30613     show: function() 
30614     {  
30615         this.el.show();   
30616     }
30617     
30618 });
30619
30620  
30621 /*
30622 * Licence: LGPL
30623 */
30624
30625 /**
30626  * @class Roo.bootstrap.UploadCropbox
30627  * @extends Roo.bootstrap.Component
30628  * Bootstrap UploadCropbox class
30629  * @cfg {String} emptyText show when image has been loaded
30630  * @cfg {String} rotateNotify show when image too small to rotate
30631  * @cfg {Number} errorTimeout default 3000
30632  * @cfg {Number} minWidth default 300
30633  * @cfg {Number} minHeight default 300
30634  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30635  * @cfg {Boolean} isDocument (true|false) default false
30636  * @cfg {String} url action url
30637  * @cfg {String} paramName default 'imageUpload'
30638  * @cfg {String} method default POST
30639  * @cfg {Boolean} loadMask (true|false) default true
30640  * @cfg {Boolean} loadingText default 'Loading...'
30641  * 
30642  * @constructor
30643  * Create a new UploadCropbox
30644  * @param {Object} config The config object
30645  */
30646
30647 Roo.bootstrap.UploadCropbox = function(config){
30648     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30649     
30650     this.addEvents({
30651         /**
30652          * @event beforeselectfile
30653          * Fire before select file
30654          * @param {Roo.bootstrap.UploadCropbox} this
30655          */
30656         "beforeselectfile" : true,
30657         /**
30658          * @event initial
30659          * Fire after initEvent
30660          * @param {Roo.bootstrap.UploadCropbox} this
30661          */
30662         "initial" : true,
30663         /**
30664          * @event crop
30665          * Fire after initEvent
30666          * @param {Roo.bootstrap.UploadCropbox} this
30667          * @param {String} data
30668          */
30669         "crop" : true,
30670         /**
30671          * @event prepare
30672          * Fire when preparing the file data
30673          * @param {Roo.bootstrap.UploadCropbox} this
30674          * @param {Object} file
30675          */
30676         "prepare" : true,
30677         /**
30678          * @event exception
30679          * Fire when get exception
30680          * @param {Roo.bootstrap.UploadCropbox} this
30681          * @param {XMLHttpRequest} xhr
30682          */
30683         "exception" : true,
30684         /**
30685          * @event beforeloadcanvas
30686          * Fire before load the canvas
30687          * @param {Roo.bootstrap.UploadCropbox} this
30688          * @param {String} src
30689          */
30690         "beforeloadcanvas" : true,
30691         /**
30692          * @event trash
30693          * Fire when trash image
30694          * @param {Roo.bootstrap.UploadCropbox} this
30695          */
30696         "trash" : true,
30697         /**
30698          * @event download
30699          * Fire when download the image
30700          * @param {Roo.bootstrap.UploadCropbox} this
30701          */
30702         "download" : true,
30703         /**
30704          * @event footerbuttonclick
30705          * Fire when footerbuttonclick
30706          * @param {Roo.bootstrap.UploadCropbox} this
30707          * @param {String} type
30708          */
30709         "footerbuttonclick" : true,
30710         /**
30711          * @event resize
30712          * Fire when resize
30713          * @param {Roo.bootstrap.UploadCropbox} this
30714          */
30715         "resize" : true,
30716         /**
30717          * @event rotate
30718          * Fire when rotate the image
30719          * @param {Roo.bootstrap.UploadCropbox} this
30720          * @param {String} pos
30721          */
30722         "rotate" : true,
30723         /**
30724          * @event inspect
30725          * Fire when inspect the file
30726          * @param {Roo.bootstrap.UploadCropbox} this
30727          * @param {Object} file
30728          */
30729         "inspect" : true,
30730         /**
30731          * @event upload
30732          * Fire when xhr upload the file
30733          * @param {Roo.bootstrap.UploadCropbox} this
30734          * @param {Object} data
30735          */
30736         "upload" : true,
30737         /**
30738          * @event arrange
30739          * Fire when arrange the file data
30740          * @param {Roo.bootstrap.UploadCropbox} this
30741          * @param {Object} formData
30742          */
30743         "arrange" : true
30744     });
30745     
30746     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30747 };
30748
30749 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30750     
30751     emptyText : 'Click to upload image',
30752     rotateNotify : 'Image is too small to rotate',
30753     errorTimeout : 3000,
30754     scale : 0,
30755     baseScale : 1,
30756     rotate : 0,
30757     dragable : false,
30758     pinching : false,
30759     mouseX : 0,
30760     mouseY : 0,
30761     cropData : false,
30762     minWidth : 300,
30763     minHeight : 300,
30764     file : false,
30765     exif : {},
30766     baseRotate : 1,
30767     cropType : 'image/jpeg',
30768     buttons : false,
30769     canvasLoaded : false,
30770     isDocument : false,
30771     method : 'POST',
30772     paramName : 'imageUpload',
30773     loadMask : true,
30774     loadingText : 'Loading...',
30775     maskEl : false,
30776     
30777     getAutoCreate : function()
30778     {
30779         var cfg = {
30780             tag : 'div',
30781             cls : 'roo-upload-cropbox',
30782             cn : [
30783                 {
30784                     tag : 'input',
30785                     cls : 'roo-upload-cropbox-selector',
30786                     type : 'file'
30787                 },
30788                 {
30789                     tag : 'div',
30790                     cls : 'roo-upload-cropbox-body',
30791                     style : 'cursor:pointer',
30792                     cn : [
30793                         {
30794                             tag : 'div',
30795                             cls : 'roo-upload-cropbox-preview'
30796                         },
30797                         {
30798                             tag : 'div',
30799                             cls : 'roo-upload-cropbox-thumb'
30800                         },
30801                         {
30802                             tag : 'div',
30803                             cls : 'roo-upload-cropbox-empty-notify',
30804                             html : this.emptyText
30805                         },
30806                         {
30807                             tag : 'div',
30808                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30809                             html : this.rotateNotify
30810                         }
30811                     ]
30812                 },
30813                 {
30814                     tag : 'div',
30815                     cls : 'roo-upload-cropbox-footer',
30816                     cn : {
30817                         tag : 'div',
30818                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30819                         cn : []
30820                     }
30821                 }
30822             ]
30823         };
30824         
30825         return cfg;
30826     },
30827     
30828     onRender : function(ct, position)
30829     {
30830         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30831         
30832         if (this.buttons.length) {
30833             
30834             Roo.each(this.buttons, function(bb) {
30835                 
30836                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30837                 
30838                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30839                 
30840             }, this);
30841         }
30842         
30843         if(this.loadMask){
30844             this.maskEl = this.el;
30845         }
30846     },
30847     
30848     initEvents : function()
30849     {
30850         this.urlAPI = (window.createObjectURL && window) || 
30851                                 (window.URL && URL.revokeObjectURL && URL) || 
30852                                 (window.webkitURL && webkitURL);
30853                         
30854         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30855         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30856         
30857         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30858         this.selectorEl.hide();
30859         
30860         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30861         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30862         
30863         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30864         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30865         this.thumbEl.hide();
30866         
30867         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30868         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30869         
30870         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30871         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30872         this.errorEl.hide();
30873         
30874         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30875         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30876         this.footerEl.hide();
30877         
30878         this.setThumbBoxSize();
30879         
30880         this.bind();
30881         
30882         this.resize();
30883         
30884         this.fireEvent('initial', this);
30885     },
30886
30887     bind : function()
30888     {
30889         var _this = this;
30890         
30891         window.addEventListener("resize", function() { _this.resize(); } );
30892         
30893         this.bodyEl.on('click', this.beforeSelectFile, this);
30894         
30895         if(Roo.isTouch){
30896             this.bodyEl.on('touchstart', this.onTouchStart, this);
30897             this.bodyEl.on('touchmove', this.onTouchMove, this);
30898             this.bodyEl.on('touchend', this.onTouchEnd, this);
30899         }
30900         
30901         if(!Roo.isTouch){
30902             this.bodyEl.on('mousedown', this.onMouseDown, this);
30903             this.bodyEl.on('mousemove', this.onMouseMove, this);
30904             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30905             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30906             Roo.get(document).on('mouseup', this.onMouseUp, this);
30907         }
30908         
30909         this.selectorEl.on('change', this.onFileSelected, this);
30910     },
30911     
30912     reset : function()
30913     {    
30914         this.scale = 0;
30915         this.baseScale = 1;
30916         this.rotate = 0;
30917         this.baseRotate = 1;
30918         this.dragable = false;
30919         this.pinching = false;
30920         this.mouseX = 0;
30921         this.mouseY = 0;
30922         this.cropData = false;
30923         this.notifyEl.dom.innerHTML = this.emptyText;
30924         
30925         this.selectorEl.dom.value = '';
30926         
30927     },
30928     
30929     resize : function()
30930     {
30931         if(this.fireEvent('resize', this) != false){
30932             this.setThumbBoxPosition();
30933             this.setCanvasPosition();
30934         }
30935     },
30936     
30937     onFooterButtonClick : function(e, el, o, type)
30938     {
30939         switch (type) {
30940             case 'rotate-left' :
30941                 this.onRotateLeft(e);
30942                 break;
30943             case 'rotate-right' :
30944                 this.onRotateRight(e);
30945                 break;
30946             case 'picture' :
30947                 this.beforeSelectFile(e);
30948                 break;
30949             case 'trash' :
30950                 this.trash(e);
30951                 break;
30952             case 'crop' :
30953                 this.crop(e);
30954                 break;
30955             case 'download' :
30956                 this.download(e);
30957                 break;
30958             default :
30959                 break;
30960         }
30961         
30962         this.fireEvent('footerbuttonclick', this, type);
30963     },
30964     
30965     beforeSelectFile : function(e)
30966     {
30967         e.preventDefault();
30968         
30969         if(this.fireEvent('beforeselectfile', this) != false){
30970             this.selectorEl.dom.click();
30971         }
30972     },
30973     
30974     onFileSelected : function(e)
30975     {
30976         e.preventDefault();
30977         
30978         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30979             return;
30980         }
30981         
30982         var file = this.selectorEl.dom.files[0];
30983         
30984         if(this.fireEvent('inspect', this, file) != false){
30985             this.prepare(file);
30986         }
30987         
30988     },
30989     
30990     trash : function(e)
30991     {
30992         this.fireEvent('trash', this);
30993     },
30994     
30995     download : function(e)
30996     {
30997         this.fireEvent('download', this);
30998     },
30999     
31000     loadCanvas : function(src)
31001     {   
31002         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31003             
31004             this.reset();
31005             
31006             this.imageEl = document.createElement('img');
31007             
31008             var _this = this;
31009             
31010             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31011             
31012             this.imageEl.src = src;
31013         }
31014     },
31015     
31016     onLoadCanvas : function()
31017     {   
31018         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31019         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31020         
31021         this.bodyEl.un('click', this.beforeSelectFile, this);
31022         
31023         this.notifyEl.hide();
31024         this.thumbEl.show();
31025         this.footerEl.show();
31026         
31027         this.baseRotateLevel();
31028         
31029         if(this.isDocument){
31030             this.setThumbBoxSize();
31031         }
31032         
31033         this.setThumbBoxPosition();
31034         
31035         this.baseScaleLevel();
31036         
31037         this.draw();
31038         
31039         this.resize();
31040         
31041         this.canvasLoaded = true;
31042         
31043         if(this.loadMask){
31044             this.maskEl.unmask();
31045         }
31046         
31047     },
31048     
31049     setCanvasPosition : function()
31050     {   
31051         if(!this.canvasEl){
31052             return;
31053         }
31054         
31055         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31056         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31057         
31058         this.previewEl.setLeft(pw);
31059         this.previewEl.setTop(ph);
31060         
31061     },
31062     
31063     onMouseDown : function(e)
31064     {   
31065         e.stopEvent();
31066         
31067         this.dragable = true;
31068         this.pinching = false;
31069         
31070         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31071             this.dragable = false;
31072             return;
31073         }
31074         
31075         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31076         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31077         
31078     },
31079     
31080     onMouseMove : function(e)
31081     {   
31082         e.stopEvent();
31083         
31084         if(!this.canvasLoaded){
31085             return;
31086         }
31087         
31088         if (!this.dragable){
31089             return;
31090         }
31091         
31092         var minX = Math.ceil(this.thumbEl.getLeft(true));
31093         var minY = Math.ceil(this.thumbEl.getTop(true));
31094         
31095         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31096         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31097         
31098         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31099         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31100         
31101         x = x - this.mouseX;
31102         y = y - this.mouseY;
31103         
31104         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31105         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31106         
31107         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31108         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31109         
31110         this.previewEl.setLeft(bgX);
31111         this.previewEl.setTop(bgY);
31112         
31113         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31114         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31115     },
31116     
31117     onMouseUp : function(e)
31118     {   
31119         e.stopEvent();
31120         
31121         this.dragable = false;
31122     },
31123     
31124     onMouseWheel : function(e)
31125     {   
31126         e.stopEvent();
31127         
31128         this.startScale = this.scale;
31129         
31130         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31131         
31132         if(!this.zoomable()){
31133             this.scale = this.startScale;
31134             return;
31135         }
31136         
31137         this.draw();
31138         
31139         return;
31140     },
31141     
31142     zoomable : function()
31143     {
31144         var minScale = this.thumbEl.getWidth() / this.minWidth;
31145         
31146         if(this.minWidth < this.minHeight){
31147             minScale = this.thumbEl.getHeight() / this.minHeight;
31148         }
31149         
31150         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31151         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31152         
31153         if(
31154                 this.isDocument &&
31155                 (this.rotate == 0 || this.rotate == 180) && 
31156                 (
31157                     width > this.imageEl.OriginWidth || 
31158                     height > this.imageEl.OriginHeight ||
31159                     (width < this.minWidth && height < this.minHeight)
31160                 )
31161         ){
31162             return false;
31163         }
31164         
31165         if(
31166                 this.isDocument &&
31167                 (this.rotate == 90 || this.rotate == 270) && 
31168                 (
31169                     width > this.imageEl.OriginWidth || 
31170                     height > this.imageEl.OriginHeight ||
31171                     (width < this.minHeight && height < this.minWidth)
31172                 )
31173         ){
31174             return false;
31175         }
31176         
31177         if(
31178                 !this.isDocument &&
31179                 (this.rotate == 0 || this.rotate == 180) && 
31180                 (
31181                     width < this.minWidth || 
31182                     width > this.imageEl.OriginWidth || 
31183                     height < this.minHeight || 
31184                     height > this.imageEl.OriginHeight
31185                 )
31186         ){
31187             return false;
31188         }
31189         
31190         if(
31191                 !this.isDocument &&
31192                 (this.rotate == 90 || this.rotate == 270) && 
31193                 (
31194                     width < this.minHeight || 
31195                     width > this.imageEl.OriginWidth || 
31196                     height < this.minWidth || 
31197                     height > this.imageEl.OriginHeight
31198                 )
31199         ){
31200             return false;
31201         }
31202         
31203         return true;
31204         
31205     },
31206     
31207     onRotateLeft : function(e)
31208     {   
31209         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31210             
31211             var minScale = this.thumbEl.getWidth() / this.minWidth;
31212             
31213             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31214             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31215             
31216             this.startScale = this.scale;
31217             
31218             while (this.getScaleLevel() < minScale){
31219             
31220                 this.scale = this.scale + 1;
31221                 
31222                 if(!this.zoomable()){
31223                     break;
31224                 }
31225                 
31226                 if(
31227                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31228                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31229                 ){
31230                     continue;
31231                 }
31232                 
31233                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31234
31235                 this.draw();
31236                 
31237                 return;
31238             }
31239             
31240             this.scale = this.startScale;
31241             
31242             this.onRotateFail();
31243             
31244             return false;
31245         }
31246         
31247         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31248
31249         if(this.isDocument){
31250             this.setThumbBoxSize();
31251             this.setThumbBoxPosition();
31252             this.setCanvasPosition();
31253         }
31254         
31255         this.draw();
31256         
31257         this.fireEvent('rotate', this, 'left');
31258         
31259     },
31260     
31261     onRotateRight : function(e)
31262     {
31263         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31264             
31265             var minScale = this.thumbEl.getWidth() / this.minWidth;
31266         
31267             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31268             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31269             
31270             this.startScale = this.scale;
31271             
31272             while (this.getScaleLevel() < minScale){
31273             
31274                 this.scale = this.scale + 1;
31275                 
31276                 if(!this.zoomable()){
31277                     break;
31278                 }
31279                 
31280                 if(
31281                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31282                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31283                 ){
31284                     continue;
31285                 }
31286                 
31287                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31288
31289                 this.draw();
31290                 
31291                 return;
31292             }
31293             
31294             this.scale = this.startScale;
31295             
31296             this.onRotateFail();
31297             
31298             return false;
31299         }
31300         
31301         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31302
31303         if(this.isDocument){
31304             this.setThumbBoxSize();
31305             this.setThumbBoxPosition();
31306             this.setCanvasPosition();
31307         }
31308         
31309         this.draw();
31310         
31311         this.fireEvent('rotate', this, 'right');
31312     },
31313     
31314     onRotateFail : function()
31315     {
31316         this.errorEl.show(true);
31317         
31318         var _this = this;
31319         
31320         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31321     },
31322     
31323     draw : function()
31324     {
31325         this.previewEl.dom.innerHTML = '';
31326         
31327         var canvasEl = document.createElement("canvas");
31328         
31329         var contextEl = canvasEl.getContext("2d");
31330         
31331         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31332         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31333         var center = this.imageEl.OriginWidth / 2;
31334         
31335         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31336             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31337             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31338             center = this.imageEl.OriginHeight / 2;
31339         }
31340         
31341         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31342         
31343         contextEl.translate(center, center);
31344         contextEl.rotate(this.rotate * Math.PI / 180);
31345
31346         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31347         
31348         this.canvasEl = document.createElement("canvas");
31349         
31350         this.contextEl = this.canvasEl.getContext("2d");
31351         
31352         switch (this.rotate) {
31353             case 0 :
31354                 
31355                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31356                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31357                 
31358                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31359                 
31360                 break;
31361             case 90 : 
31362                 
31363                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31364                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31365                 
31366                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31367                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31368                     break;
31369                 }
31370                 
31371                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31372                 
31373                 break;
31374             case 180 :
31375                 
31376                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31377                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31378                 
31379                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31380                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31381                     break;
31382                 }
31383                 
31384                 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);
31385                 
31386                 break;
31387             case 270 :
31388                 
31389                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31390                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31391         
31392                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31393                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31394                     break;
31395                 }
31396                 
31397                 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);
31398                 
31399                 break;
31400             default : 
31401                 break;
31402         }
31403         
31404         this.previewEl.appendChild(this.canvasEl);
31405         
31406         this.setCanvasPosition();
31407     },
31408     
31409     crop : function()
31410     {
31411         if(!this.canvasLoaded){
31412             return;
31413         }
31414         
31415         var imageCanvas = document.createElement("canvas");
31416         
31417         var imageContext = imageCanvas.getContext("2d");
31418         
31419         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31420         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31421         
31422         var center = imageCanvas.width / 2;
31423         
31424         imageContext.translate(center, center);
31425         
31426         imageContext.rotate(this.rotate * Math.PI / 180);
31427         
31428         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31429         
31430         var canvas = document.createElement("canvas");
31431         
31432         var context = canvas.getContext("2d");
31433                 
31434         canvas.width = this.minWidth;
31435         canvas.height = this.minHeight;
31436
31437         switch (this.rotate) {
31438             case 0 :
31439                 
31440                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31441                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31442                 
31443                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31444                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31445                 
31446                 var targetWidth = this.minWidth - 2 * x;
31447                 var targetHeight = this.minHeight - 2 * y;
31448                 
31449                 var scale = 1;
31450                 
31451                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31452                     scale = targetWidth / width;
31453                 }
31454                 
31455                 if(x > 0 && y == 0){
31456                     scale = targetHeight / height;
31457                 }
31458                 
31459                 if(x > 0 && y > 0){
31460                     scale = targetWidth / width;
31461                     
31462                     if(width < height){
31463                         scale = targetHeight / height;
31464                     }
31465                 }
31466                 
31467                 context.scale(scale, scale);
31468                 
31469                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31470                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31471
31472                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31473                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31474
31475                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31476                 
31477                 break;
31478             case 90 : 
31479                 
31480                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31481                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31482                 
31483                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31484                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31485                 
31486                 var targetWidth = this.minWidth - 2 * x;
31487                 var targetHeight = this.minHeight - 2 * y;
31488                 
31489                 var scale = 1;
31490                 
31491                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31492                     scale = targetWidth / width;
31493                 }
31494                 
31495                 if(x > 0 && y == 0){
31496                     scale = targetHeight / height;
31497                 }
31498                 
31499                 if(x > 0 && y > 0){
31500                     scale = targetWidth / width;
31501                     
31502                     if(width < height){
31503                         scale = targetHeight / height;
31504                     }
31505                 }
31506                 
31507                 context.scale(scale, scale);
31508                 
31509                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31510                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31511
31512                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31513                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31514                 
31515                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31516                 
31517                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31518                 
31519                 break;
31520             case 180 :
31521                 
31522                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31523                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31524                 
31525                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31526                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31527                 
31528                 var targetWidth = this.minWidth - 2 * x;
31529                 var targetHeight = this.minHeight - 2 * y;
31530                 
31531                 var scale = 1;
31532                 
31533                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31534                     scale = targetWidth / width;
31535                 }
31536                 
31537                 if(x > 0 && y == 0){
31538                     scale = targetHeight / height;
31539                 }
31540                 
31541                 if(x > 0 && y > 0){
31542                     scale = targetWidth / width;
31543                     
31544                     if(width < height){
31545                         scale = targetHeight / height;
31546                     }
31547                 }
31548                 
31549                 context.scale(scale, scale);
31550                 
31551                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31552                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31553
31554                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31555                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31556
31557                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31558                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31559                 
31560                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31561                 
31562                 break;
31563             case 270 :
31564                 
31565                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31566                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31567                 
31568                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31569                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31570                 
31571                 var targetWidth = this.minWidth - 2 * x;
31572                 var targetHeight = this.minHeight - 2 * y;
31573                 
31574                 var scale = 1;
31575                 
31576                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31577                     scale = targetWidth / width;
31578                 }
31579                 
31580                 if(x > 0 && y == 0){
31581                     scale = targetHeight / height;
31582                 }
31583                 
31584                 if(x > 0 && y > 0){
31585                     scale = targetWidth / width;
31586                     
31587                     if(width < height){
31588                         scale = targetHeight / height;
31589                     }
31590                 }
31591                 
31592                 context.scale(scale, scale);
31593                 
31594                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31595                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31596
31597                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31598                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31599                 
31600                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31601                 
31602                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31603                 
31604                 break;
31605             default : 
31606                 break;
31607         }
31608         
31609         this.cropData = canvas.toDataURL(this.cropType);
31610         
31611         if(this.fireEvent('crop', this, this.cropData) !== false){
31612             this.process(this.file, this.cropData);
31613         }
31614         
31615         return;
31616         
31617     },
31618     
31619     setThumbBoxSize : function()
31620     {
31621         var width, height;
31622         
31623         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31624             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31625             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31626             
31627             this.minWidth = width;
31628             this.minHeight = height;
31629             
31630             if(this.rotate == 90 || this.rotate == 270){
31631                 this.minWidth = height;
31632                 this.minHeight = width;
31633             }
31634         }
31635         
31636         height = 300;
31637         width = Math.ceil(this.minWidth * height / this.minHeight);
31638         
31639         if(this.minWidth > this.minHeight){
31640             width = 300;
31641             height = Math.ceil(this.minHeight * width / this.minWidth);
31642         }
31643         
31644         this.thumbEl.setStyle({
31645             width : width + 'px',
31646             height : height + 'px'
31647         });
31648
31649         return;
31650             
31651     },
31652     
31653     setThumbBoxPosition : function()
31654     {
31655         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31656         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31657         
31658         this.thumbEl.setLeft(x);
31659         this.thumbEl.setTop(y);
31660         
31661     },
31662     
31663     baseRotateLevel : function()
31664     {
31665         this.baseRotate = 1;
31666         
31667         if(
31668                 typeof(this.exif) != 'undefined' &&
31669                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31670                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31671         ){
31672             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31673         }
31674         
31675         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31676         
31677     },
31678     
31679     baseScaleLevel : function()
31680     {
31681         var width, height;
31682         
31683         if(this.isDocument){
31684             
31685             if(this.baseRotate == 6 || this.baseRotate == 8){
31686             
31687                 height = this.thumbEl.getHeight();
31688                 this.baseScale = height / this.imageEl.OriginWidth;
31689
31690                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31691                     width = this.thumbEl.getWidth();
31692                     this.baseScale = width / this.imageEl.OriginHeight;
31693                 }
31694
31695                 return;
31696             }
31697
31698             height = this.thumbEl.getHeight();
31699             this.baseScale = height / this.imageEl.OriginHeight;
31700
31701             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31702                 width = this.thumbEl.getWidth();
31703                 this.baseScale = width / this.imageEl.OriginWidth;
31704             }
31705
31706             return;
31707         }
31708         
31709         if(this.baseRotate == 6 || this.baseRotate == 8){
31710             
31711             width = this.thumbEl.getHeight();
31712             this.baseScale = width / this.imageEl.OriginHeight;
31713             
31714             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31715                 height = this.thumbEl.getWidth();
31716                 this.baseScale = height / this.imageEl.OriginHeight;
31717             }
31718             
31719             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31720                 height = this.thumbEl.getWidth();
31721                 this.baseScale = height / this.imageEl.OriginHeight;
31722                 
31723                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31724                     width = this.thumbEl.getHeight();
31725                     this.baseScale = width / this.imageEl.OriginWidth;
31726                 }
31727             }
31728             
31729             return;
31730         }
31731         
31732         width = this.thumbEl.getWidth();
31733         this.baseScale = width / this.imageEl.OriginWidth;
31734         
31735         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31736             height = this.thumbEl.getHeight();
31737             this.baseScale = height / this.imageEl.OriginHeight;
31738         }
31739         
31740         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31741             
31742             height = this.thumbEl.getHeight();
31743             this.baseScale = height / this.imageEl.OriginHeight;
31744             
31745             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31746                 width = this.thumbEl.getWidth();
31747                 this.baseScale = width / this.imageEl.OriginWidth;
31748             }
31749             
31750         }
31751         
31752         return;
31753     },
31754     
31755     getScaleLevel : function()
31756     {
31757         return this.baseScale * Math.pow(1.1, this.scale);
31758     },
31759     
31760     onTouchStart : function(e)
31761     {
31762         if(!this.canvasLoaded){
31763             this.beforeSelectFile(e);
31764             return;
31765         }
31766         
31767         var touches = e.browserEvent.touches;
31768         
31769         if(!touches){
31770             return;
31771         }
31772         
31773         if(touches.length == 1){
31774             this.onMouseDown(e);
31775             return;
31776         }
31777         
31778         if(touches.length != 2){
31779             return;
31780         }
31781         
31782         var coords = [];
31783         
31784         for(var i = 0, finger; finger = touches[i]; i++){
31785             coords.push(finger.pageX, finger.pageY);
31786         }
31787         
31788         var x = Math.pow(coords[0] - coords[2], 2);
31789         var y = Math.pow(coords[1] - coords[3], 2);
31790         
31791         this.startDistance = Math.sqrt(x + y);
31792         
31793         this.startScale = this.scale;
31794         
31795         this.pinching = true;
31796         this.dragable = false;
31797         
31798     },
31799     
31800     onTouchMove : function(e)
31801     {
31802         if(!this.pinching && !this.dragable){
31803             return;
31804         }
31805         
31806         var touches = e.browserEvent.touches;
31807         
31808         if(!touches){
31809             return;
31810         }
31811         
31812         if(this.dragable){
31813             this.onMouseMove(e);
31814             return;
31815         }
31816         
31817         var coords = [];
31818         
31819         for(var i = 0, finger; finger = touches[i]; i++){
31820             coords.push(finger.pageX, finger.pageY);
31821         }
31822         
31823         var x = Math.pow(coords[0] - coords[2], 2);
31824         var y = Math.pow(coords[1] - coords[3], 2);
31825         
31826         this.endDistance = Math.sqrt(x + y);
31827         
31828         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31829         
31830         if(!this.zoomable()){
31831             this.scale = this.startScale;
31832             return;
31833         }
31834         
31835         this.draw();
31836         
31837     },
31838     
31839     onTouchEnd : function(e)
31840     {
31841         this.pinching = false;
31842         this.dragable = false;
31843         
31844     },
31845     
31846     process : function(file, crop)
31847     {
31848         if(this.loadMask){
31849             this.maskEl.mask(this.loadingText);
31850         }
31851         
31852         this.xhr = new XMLHttpRequest();
31853         
31854         file.xhr = this.xhr;
31855
31856         this.xhr.open(this.method, this.url, true);
31857         
31858         var headers = {
31859             "Accept": "application/json",
31860             "Cache-Control": "no-cache",
31861             "X-Requested-With": "XMLHttpRequest"
31862         };
31863         
31864         for (var headerName in headers) {
31865             var headerValue = headers[headerName];
31866             if (headerValue) {
31867                 this.xhr.setRequestHeader(headerName, headerValue);
31868             }
31869         }
31870         
31871         var _this = this;
31872         
31873         this.xhr.onload = function()
31874         {
31875             _this.xhrOnLoad(_this.xhr);
31876         }
31877         
31878         this.xhr.onerror = function()
31879         {
31880             _this.xhrOnError(_this.xhr);
31881         }
31882         
31883         var formData = new FormData();
31884
31885         formData.append('returnHTML', 'NO');
31886         
31887         if(crop){
31888             formData.append('crop', crop);
31889         }
31890         
31891         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31892             formData.append(this.paramName, file, file.name);
31893         }
31894         
31895         if(typeof(file.filename) != 'undefined'){
31896             formData.append('filename', file.filename);
31897         }
31898         
31899         if(typeof(file.mimetype) != 'undefined'){
31900             formData.append('mimetype', file.mimetype);
31901         }
31902         
31903         if(this.fireEvent('arrange', this, formData) != false){
31904             this.xhr.send(formData);
31905         };
31906     },
31907     
31908     xhrOnLoad : function(xhr)
31909     {
31910         if(this.loadMask){
31911             this.maskEl.unmask();
31912         }
31913         
31914         if (xhr.readyState !== 4) {
31915             this.fireEvent('exception', this, xhr);
31916             return;
31917         }
31918
31919         var response = Roo.decode(xhr.responseText);
31920         
31921         if(!response.success){
31922             this.fireEvent('exception', this, xhr);
31923             return;
31924         }
31925         
31926         var response = Roo.decode(xhr.responseText);
31927         
31928         this.fireEvent('upload', this, response);
31929         
31930     },
31931     
31932     xhrOnError : function()
31933     {
31934         if(this.loadMask){
31935             this.maskEl.unmask();
31936         }
31937         
31938         Roo.log('xhr on error');
31939         
31940         var response = Roo.decode(xhr.responseText);
31941           
31942         Roo.log(response);
31943         
31944     },
31945     
31946     prepare : function(file)
31947     {   
31948         if(this.loadMask){
31949             this.maskEl.mask(this.loadingText);
31950         }
31951         
31952         this.file = false;
31953         this.exif = {};
31954         
31955         if(typeof(file) === 'string'){
31956             this.loadCanvas(file);
31957             return;
31958         }
31959         
31960         if(!file || !this.urlAPI){
31961             return;
31962         }
31963         
31964         this.file = file;
31965         this.cropType = file.type;
31966         
31967         var _this = this;
31968         
31969         if(this.fireEvent('prepare', this, this.file) != false){
31970             
31971             var reader = new FileReader();
31972             
31973             reader.onload = function (e) {
31974                 if (e.target.error) {
31975                     Roo.log(e.target.error);
31976                     return;
31977                 }
31978                 
31979                 var buffer = e.target.result,
31980                     dataView = new DataView(buffer),
31981                     offset = 2,
31982                     maxOffset = dataView.byteLength - 4,
31983                     markerBytes,
31984                     markerLength;
31985                 
31986                 if (dataView.getUint16(0) === 0xffd8) {
31987                     while (offset < maxOffset) {
31988                         markerBytes = dataView.getUint16(offset);
31989                         
31990                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31991                             markerLength = dataView.getUint16(offset + 2) + 2;
31992                             if (offset + markerLength > dataView.byteLength) {
31993                                 Roo.log('Invalid meta data: Invalid segment size.');
31994                                 break;
31995                             }
31996                             
31997                             if(markerBytes == 0xffe1){
31998                                 _this.parseExifData(
31999                                     dataView,
32000                                     offset,
32001                                     markerLength
32002                                 );
32003                             }
32004                             
32005                             offset += markerLength;
32006                             
32007                             continue;
32008                         }
32009                         
32010                         break;
32011                     }
32012                     
32013                 }
32014                 
32015                 var url = _this.urlAPI.createObjectURL(_this.file);
32016                 
32017                 _this.loadCanvas(url);
32018                 
32019                 return;
32020             }
32021             
32022             reader.readAsArrayBuffer(this.file);
32023             
32024         }
32025         
32026     },
32027     
32028     parseExifData : function(dataView, offset, length)
32029     {
32030         var tiffOffset = offset + 10,
32031             littleEndian,
32032             dirOffset;
32033     
32034         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32035             // No Exif data, might be XMP data instead
32036             return;
32037         }
32038         
32039         // Check for the ASCII code for "Exif" (0x45786966):
32040         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32041             // No Exif data, might be XMP data instead
32042             return;
32043         }
32044         if (tiffOffset + 8 > dataView.byteLength) {
32045             Roo.log('Invalid Exif data: Invalid segment size.');
32046             return;
32047         }
32048         // Check for the two null bytes:
32049         if (dataView.getUint16(offset + 8) !== 0x0000) {
32050             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32051             return;
32052         }
32053         // Check the byte alignment:
32054         switch (dataView.getUint16(tiffOffset)) {
32055         case 0x4949:
32056             littleEndian = true;
32057             break;
32058         case 0x4D4D:
32059             littleEndian = false;
32060             break;
32061         default:
32062             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32063             return;
32064         }
32065         // Check for the TIFF tag marker (0x002A):
32066         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32067             Roo.log('Invalid Exif data: Missing TIFF marker.');
32068             return;
32069         }
32070         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32071         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32072         
32073         this.parseExifTags(
32074             dataView,
32075             tiffOffset,
32076             tiffOffset + dirOffset,
32077             littleEndian
32078         );
32079     },
32080     
32081     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32082     {
32083         var tagsNumber,
32084             dirEndOffset,
32085             i;
32086         if (dirOffset + 6 > dataView.byteLength) {
32087             Roo.log('Invalid Exif data: Invalid directory offset.');
32088             return;
32089         }
32090         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32091         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32092         if (dirEndOffset + 4 > dataView.byteLength) {
32093             Roo.log('Invalid Exif data: Invalid directory size.');
32094             return;
32095         }
32096         for (i = 0; i < tagsNumber; i += 1) {
32097             this.parseExifTag(
32098                 dataView,
32099                 tiffOffset,
32100                 dirOffset + 2 + 12 * i, // tag offset
32101                 littleEndian
32102             );
32103         }
32104         // Return the offset to the next directory:
32105         return dataView.getUint32(dirEndOffset, littleEndian);
32106     },
32107     
32108     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32109     {
32110         var tag = dataView.getUint16(offset, littleEndian);
32111         
32112         this.exif[tag] = this.getExifValue(
32113             dataView,
32114             tiffOffset,
32115             offset,
32116             dataView.getUint16(offset + 2, littleEndian), // tag type
32117             dataView.getUint32(offset + 4, littleEndian), // tag length
32118             littleEndian
32119         );
32120     },
32121     
32122     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32123     {
32124         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32125             tagSize,
32126             dataOffset,
32127             values,
32128             i,
32129             str,
32130             c;
32131     
32132         if (!tagType) {
32133             Roo.log('Invalid Exif data: Invalid tag type.');
32134             return;
32135         }
32136         
32137         tagSize = tagType.size * length;
32138         // Determine if the value is contained in the dataOffset bytes,
32139         // or if the value at the dataOffset is a pointer to the actual data:
32140         dataOffset = tagSize > 4 ?
32141                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32142         if (dataOffset + tagSize > dataView.byteLength) {
32143             Roo.log('Invalid Exif data: Invalid data offset.');
32144             return;
32145         }
32146         if (length === 1) {
32147             return tagType.getValue(dataView, dataOffset, littleEndian);
32148         }
32149         values = [];
32150         for (i = 0; i < length; i += 1) {
32151             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32152         }
32153         
32154         if (tagType.ascii) {
32155             str = '';
32156             // Concatenate the chars:
32157             for (i = 0; i < values.length; i += 1) {
32158                 c = values[i];
32159                 // Ignore the terminating NULL byte(s):
32160                 if (c === '\u0000') {
32161                     break;
32162                 }
32163                 str += c;
32164             }
32165             return str;
32166         }
32167         return values;
32168     }
32169     
32170 });
32171
32172 Roo.apply(Roo.bootstrap.UploadCropbox, {
32173     tags : {
32174         'Orientation': 0x0112
32175     },
32176     
32177     Orientation: {
32178             1: 0, //'top-left',
32179 //            2: 'top-right',
32180             3: 180, //'bottom-right',
32181 //            4: 'bottom-left',
32182 //            5: 'left-top',
32183             6: 90, //'right-top',
32184 //            7: 'right-bottom',
32185             8: 270 //'left-bottom'
32186     },
32187     
32188     exifTagTypes : {
32189         // byte, 8-bit unsigned int:
32190         1: {
32191             getValue: function (dataView, dataOffset) {
32192                 return dataView.getUint8(dataOffset);
32193             },
32194             size: 1
32195         },
32196         // ascii, 8-bit byte:
32197         2: {
32198             getValue: function (dataView, dataOffset) {
32199                 return String.fromCharCode(dataView.getUint8(dataOffset));
32200             },
32201             size: 1,
32202             ascii: true
32203         },
32204         // short, 16 bit int:
32205         3: {
32206             getValue: function (dataView, dataOffset, littleEndian) {
32207                 return dataView.getUint16(dataOffset, littleEndian);
32208             },
32209             size: 2
32210         },
32211         // long, 32 bit int:
32212         4: {
32213             getValue: function (dataView, dataOffset, littleEndian) {
32214                 return dataView.getUint32(dataOffset, littleEndian);
32215             },
32216             size: 4
32217         },
32218         // rational = two long values, first is numerator, second is denominator:
32219         5: {
32220             getValue: function (dataView, dataOffset, littleEndian) {
32221                 return dataView.getUint32(dataOffset, littleEndian) /
32222                     dataView.getUint32(dataOffset + 4, littleEndian);
32223             },
32224             size: 8
32225         },
32226         // slong, 32 bit signed int:
32227         9: {
32228             getValue: function (dataView, dataOffset, littleEndian) {
32229                 return dataView.getInt32(dataOffset, littleEndian);
32230             },
32231             size: 4
32232         },
32233         // srational, two slongs, first is numerator, second is denominator:
32234         10: {
32235             getValue: function (dataView, dataOffset, littleEndian) {
32236                 return dataView.getInt32(dataOffset, littleEndian) /
32237                     dataView.getInt32(dataOffset + 4, littleEndian);
32238             },
32239             size: 8
32240         }
32241     },
32242     
32243     footer : {
32244         STANDARD : [
32245             {
32246                 tag : 'div',
32247                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32248                 action : 'rotate-left',
32249                 cn : [
32250                     {
32251                         tag : 'button',
32252                         cls : 'btn btn-default',
32253                         html : '<i class="fa fa-undo"></i>'
32254                     }
32255                 ]
32256             },
32257             {
32258                 tag : 'div',
32259                 cls : 'btn-group roo-upload-cropbox-picture',
32260                 action : 'picture',
32261                 cn : [
32262                     {
32263                         tag : 'button',
32264                         cls : 'btn btn-default',
32265                         html : '<i class="fa fa-picture-o"></i>'
32266                     }
32267                 ]
32268             },
32269             {
32270                 tag : 'div',
32271                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32272                 action : 'rotate-right',
32273                 cn : [
32274                     {
32275                         tag : 'button',
32276                         cls : 'btn btn-default',
32277                         html : '<i class="fa fa-repeat"></i>'
32278                     }
32279                 ]
32280             }
32281         ],
32282         DOCUMENT : [
32283             {
32284                 tag : 'div',
32285                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32286                 action : 'rotate-left',
32287                 cn : [
32288                     {
32289                         tag : 'button',
32290                         cls : 'btn btn-default',
32291                         html : '<i class="fa fa-undo"></i>'
32292                     }
32293                 ]
32294             },
32295             {
32296                 tag : 'div',
32297                 cls : 'btn-group roo-upload-cropbox-download',
32298                 action : 'download',
32299                 cn : [
32300                     {
32301                         tag : 'button',
32302                         cls : 'btn btn-default',
32303                         html : '<i class="fa fa-download"></i>'
32304                     }
32305                 ]
32306             },
32307             {
32308                 tag : 'div',
32309                 cls : 'btn-group roo-upload-cropbox-crop',
32310                 action : 'crop',
32311                 cn : [
32312                     {
32313                         tag : 'button',
32314                         cls : 'btn btn-default',
32315                         html : '<i class="fa fa-crop"></i>'
32316                     }
32317                 ]
32318             },
32319             {
32320                 tag : 'div',
32321                 cls : 'btn-group roo-upload-cropbox-trash',
32322                 action : 'trash',
32323                 cn : [
32324                     {
32325                         tag : 'button',
32326                         cls : 'btn btn-default',
32327                         html : '<i class="fa fa-trash"></i>'
32328                     }
32329                 ]
32330             },
32331             {
32332                 tag : 'div',
32333                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32334                 action : 'rotate-right',
32335                 cn : [
32336                     {
32337                         tag : 'button',
32338                         cls : 'btn btn-default',
32339                         html : '<i class="fa fa-repeat"></i>'
32340                     }
32341                 ]
32342             }
32343         ],
32344         ROTATOR : [
32345             {
32346                 tag : 'div',
32347                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32348                 action : 'rotate-left',
32349                 cn : [
32350                     {
32351                         tag : 'button',
32352                         cls : 'btn btn-default',
32353                         html : '<i class="fa fa-undo"></i>'
32354                     }
32355                 ]
32356             },
32357             {
32358                 tag : 'div',
32359                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32360                 action : 'rotate-right',
32361                 cn : [
32362                     {
32363                         tag : 'button',
32364                         cls : 'btn btn-default',
32365                         html : '<i class="fa fa-repeat"></i>'
32366                     }
32367                 ]
32368             }
32369         ]
32370     }
32371 });
32372
32373 /*
32374 * Licence: LGPL
32375 */
32376
32377 /**
32378  * @class Roo.bootstrap.DocumentManager
32379  * @extends Roo.bootstrap.Component
32380  * Bootstrap DocumentManager class
32381  * @cfg {String} paramName default 'imageUpload'
32382  * @cfg {String} toolTipName default 'filename'
32383  * @cfg {String} method default POST
32384  * @cfg {String} url action url
32385  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32386  * @cfg {Boolean} multiple multiple upload default true
32387  * @cfg {Number} thumbSize default 300
32388  * @cfg {String} fieldLabel
32389  * @cfg {Number} labelWidth default 4
32390  * @cfg {String} labelAlign (left|top) default left
32391  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32392 * @cfg {Number} labellg set the width of label (1-12)
32393  * @cfg {Number} labelmd set the width of label (1-12)
32394  * @cfg {Number} labelsm set the width of label (1-12)
32395  * @cfg {Number} labelxs set the width of label (1-12)
32396  * 
32397  * @constructor
32398  * Create a new DocumentManager
32399  * @param {Object} config The config object
32400  */
32401
32402 Roo.bootstrap.DocumentManager = function(config){
32403     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32404     
32405     this.files = [];
32406     this.delegates = [];
32407     
32408     this.addEvents({
32409         /**
32410          * @event initial
32411          * Fire when initial the DocumentManager
32412          * @param {Roo.bootstrap.DocumentManager} this
32413          */
32414         "initial" : true,
32415         /**
32416          * @event inspect
32417          * inspect selected file
32418          * @param {Roo.bootstrap.DocumentManager} this
32419          * @param {File} file
32420          */
32421         "inspect" : true,
32422         /**
32423          * @event exception
32424          * Fire when xhr load exception
32425          * @param {Roo.bootstrap.DocumentManager} this
32426          * @param {XMLHttpRequest} xhr
32427          */
32428         "exception" : true,
32429         /**
32430          * @event afterupload
32431          * Fire when xhr load exception
32432          * @param {Roo.bootstrap.DocumentManager} this
32433          * @param {XMLHttpRequest} xhr
32434          */
32435         "afterupload" : true,
32436         /**
32437          * @event prepare
32438          * prepare the form data
32439          * @param {Roo.bootstrap.DocumentManager} this
32440          * @param {Object} formData
32441          */
32442         "prepare" : true,
32443         /**
32444          * @event remove
32445          * Fire when remove the file
32446          * @param {Roo.bootstrap.DocumentManager} this
32447          * @param {Object} file
32448          */
32449         "remove" : true,
32450         /**
32451          * @event refresh
32452          * Fire after refresh the file
32453          * @param {Roo.bootstrap.DocumentManager} this
32454          */
32455         "refresh" : true,
32456         /**
32457          * @event click
32458          * Fire after click the image
32459          * @param {Roo.bootstrap.DocumentManager} this
32460          * @param {Object} file
32461          */
32462         "click" : true,
32463         /**
32464          * @event edit
32465          * Fire when upload a image and editable set to true
32466          * @param {Roo.bootstrap.DocumentManager} this
32467          * @param {Object} file
32468          */
32469         "edit" : true,
32470         /**
32471          * @event beforeselectfile
32472          * Fire before select file
32473          * @param {Roo.bootstrap.DocumentManager} this
32474          */
32475         "beforeselectfile" : true,
32476         /**
32477          * @event process
32478          * Fire before process file
32479          * @param {Roo.bootstrap.DocumentManager} this
32480          * @param {Object} file
32481          */
32482         "process" : true,
32483         /**
32484          * @event previewrendered
32485          * Fire when preview rendered
32486          * @param {Roo.bootstrap.DocumentManager} this
32487          * @param {Object} file
32488          */
32489         "previewrendered" : true,
32490         /**
32491          */
32492         "previewResize" : true
32493         
32494     });
32495 };
32496
32497 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32498     
32499     boxes : 0,
32500     inputName : '',
32501     thumbSize : 300,
32502     multiple : true,
32503     files : false,
32504     method : 'POST',
32505     url : '',
32506     paramName : 'imageUpload',
32507     toolTipName : 'filename',
32508     fieldLabel : '',
32509     labelWidth : 4,
32510     labelAlign : 'left',
32511     editable : true,
32512     delegates : false,
32513     xhr : false, 
32514     
32515     labellg : 0,
32516     labelmd : 0,
32517     labelsm : 0,
32518     labelxs : 0,
32519     
32520     getAutoCreate : function()
32521     {   
32522         var managerWidget = {
32523             tag : 'div',
32524             cls : 'roo-document-manager',
32525             cn : [
32526                 {
32527                     tag : 'input',
32528                     cls : 'roo-document-manager-selector',
32529                     type : 'file'
32530                 },
32531                 {
32532                     tag : 'div',
32533                     cls : 'roo-document-manager-uploader',
32534                     cn : [
32535                         {
32536                             tag : 'div',
32537                             cls : 'roo-document-manager-upload-btn',
32538                             html : '<i class="fa fa-plus"></i>'
32539                         }
32540                     ]
32541                     
32542                 }
32543             ]
32544         };
32545         
32546         var content = [
32547             {
32548                 tag : 'div',
32549                 cls : 'column col-md-12',
32550                 cn : managerWidget
32551             }
32552         ];
32553         
32554         if(this.fieldLabel.length){
32555             
32556             content = [
32557                 {
32558                     tag : 'div',
32559                     cls : 'column col-md-12',
32560                     html : this.fieldLabel
32561                 },
32562                 {
32563                     tag : 'div',
32564                     cls : 'column col-md-12',
32565                     cn : managerWidget
32566                 }
32567             ];
32568
32569             if(this.labelAlign == 'left'){
32570                 content = [
32571                     {
32572                         tag : 'div',
32573                         cls : 'column',
32574                         html : this.fieldLabel
32575                     },
32576                     {
32577                         tag : 'div',
32578                         cls : 'column',
32579                         cn : managerWidget
32580                     }
32581                 ];
32582                 
32583                 if(this.labelWidth > 12){
32584                     content[0].style = "width: " + this.labelWidth + 'px';
32585                 }
32586
32587                 if(this.labelWidth < 13 && this.labelmd == 0){
32588                     this.labelmd = this.labelWidth;
32589                 }
32590
32591                 if(this.labellg > 0){
32592                     content[0].cls += ' col-lg-' + this.labellg;
32593                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32594                 }
32595
32596                 if(this.labelmd > 0){
32597                     content[0].cls += ' col-md-' + this.labelmd;
32598                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32599                 }
32600
32601                 if(this.labelsm > 0){
32602                     content[0].cls += ' col-sm-' + this.labelsm;
32603                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32604                 }
32605
32606                 if(this.labelxs > 0){
32607                     content[0].cls += ' col-xs-' + this.labelxs;
32608                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32609                 }
32610                 
32611             }
32612         }
32613         
32614         var cfg = {
32615             tag : 'div',
32616             cls : 'row clearfix',
32617             cn : content
32618         };
32619         
32620         return cfg;
32621         
32622     },
32623     
32624     initEvents : function()
32625     {
32626         this.managerEl = this.el.select('.roo-document-manager', true).first();
32627         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32628         
32629         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32630         this.selectorEl.hide();
32631         
32632         if(this.multiple){
32633             this.selectorEl.attr('multiple', 'multiple');
32634         }
32635         
32636         this.selectorEl.on('change', this.onFileSelected, this);
32637         
32638         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32639         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32640         
32641         this.uploader.on('click', this.onUploaderClick, this);
32642         
32643         this.renderProgressDialog();
32644         
32645         var _this = this;
32646         
32647         window.addEventListener("resize", function() { _this.refresh(); } );
32648         
32649         this.fireEvent('initial', this);
32650     },
32651     
32652     renderProgressDialog : function()
32653     {
32654         var _this = this;
32655         
32656         this.progressDialog = new Roo.bootstrap.Modal({
32657             cls : 'roo-document-manager-progress-dialog',
32658             allow_close : false,
32659             animate : false,
32660             title : '',
32661             buttons : [
32662                 {
32663                     name  :'cancel',
32664                     weight : 'danger',
32665                     html : 'Cancel'
32666                 }
32667             ], 
32668             listeners : { 
32669                 btnclick : function() {
32670                     _this.uploadCancel();
32671                     this.hide();
32672                 }
32673             }
32674         });
32675          
32676         this.progressDialog.render(Roo.get(document.body));
32677          
32678         this.progress = new Roo.bootstrap.Progress({
32679             cls : 'roo-document-manager-progress',
32680             active : true,
32681             striped : true
32682         });
32683         
32684         this.progress.render(this.progressDialog.getChildContainer());
32685         
32686         this.progressBar = new Roo.bootstrap.ProgressBar({
32687             cls : 'roo-document-manager-progress-bar',
32688             aria_valuenow : 0,
32689             aria_valuemin : 0,
32690             aria_valuemax : 12,
32691             panel : 'success'
32692         });
32693         
32694         this.progressBar.render(this.progress.getChildContainer());
32695     },
32696     
32697     onUploaderClick : function(e)
32698     {
32699         e.preventDefault();
32700      
32701         if(this.fireEvent('beforeselectfile', this) != false){
32702             this.selectorEl.dom.click();
32703         }
32704         
32705     },
32706     
32707     onFileSelected : function(e)
32708     {
32709         e.preventDefault();
32710         
32711         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32712             return;
32713         }
32714         
32715         Roo.each(this.selectorEl.dom.files, function(file){
32716             if(this.fireEvent('inspect', this, file) != false){
32717                 this.files.push(file);
32718             }
32719         }, this);
32720         
32721         this.queue();
32722         
32723     },
32724     
32725     queue : function()
32726     {
32727         this.selectorEl.dom.value = '';
32728         
32729         if(!this.files || !this.files.length){
32730             return;
32731         }
32732         
32733         if(this.boxes > 0 && this.files.length > this.boxes){
32734             this.files = this.files.slice(0, this.boxes);
32735         }
32736         
32737         this.uploader.show();
32738         
32739         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32740             this.uploader.hide();
32741         }
32742         
32743         var _this = this;
32744         
32745         var files = [];
32746         
32747         var docs = [];
32748         
32749         Roo.each(this.files, function(file){
32750             
32751             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32752                 var f = this.renderPreview(file);
32753                 files.push(f);
32754                 return;
32755             }
32756             
32757             if(file.type.indexOf('image') != -1){
32758                 this.delegates.push(
32759                     (function(){
32760                         _this.process(file);
32761                     }).createDelegate(this)
32762                 );
32763         
32764                 return;
32765             }
32766             
32767             docs.push(
32768                 (function(){
32769                     _this.process(file);
32770                 }).createDelegate(this)
32771             );
32772             
32773         }, this);
32774         
32775         this.files = files;
32776         
32777         this.delegates = this.delegates.concat(docs);
32778         
32779         if(!this.delegates.length){
32780             this.refresh();
32781             return;
32782         }
32783         
32784         this.progressBar.aria_valuemax = this.delegates.length;
32785         
32786         this.arrange();
32787         
32788         return;
32789     },
32790     
32791     arrange : function()
32792     {
32793         if(!this.delegates.length){
32794             this.progressDialog.hide();
32795             this.refresh();
32796             return;
32797         }
32798         
32799         var delegate = this.delegates.shift();
32800         
32801         this.progressDialog.show();
32802         
32803         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32804         
32805         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32806         
32807         delegate();
32808     },
32809     
32810     refresh : function()
32811     {
32812         this.uploader.show();
32813         
32814         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32815             this.uploader.hide();
32816         }
32817         
32818         Roo.isTouch ? this.closable(false) : this.closable(true);
32819         
32820         this.fireEvent('refresh', this);
32821     },
32822     
32823     onRemove : function(e, el, o)
32824     {
32825         e.preventDefault();
32826         
32827         this.fireEvent('remove', this, o);
32828         
32829     },
32830     
32831     remove : function(o)
32832     {
32833         var files = [];
32834         
32835         Roo.each(this.files, function(file){
32836             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32837                 files.push(file);
32838                 return;
32839             }
32840
32841             o.target.remove();
32842
32843         }, this);
32844         
32845         this.files = files;
32846         
32847         this.refresh();
32848     },
32849     
32850     clear : function()
32851     {
32852         Roo.each(this.files, function(file){
32853             if(!file.target){
32854                 return;
32855             }
32856             
32857             file.target.remove();
32858
32859         }, this);
32860         
32861         this.files = [];
32862         
32863         this.refresh();
32864     },
32865     
32866     onClick : function(e, el, o)
32867     {
32868         e.preventDefault();
32869         
32870         this.fireEvent('click', this, o);
32871         
32872     },
32873     
32874     closable : function(closable)
32875     {
32876         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32877             
32878             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32879             
32880             if(closable){
32881                 el.show();
32882                 return;
32883             }
32884             
32885             el.hide();
32886             
32887         }, this);
32888     },
32889     
32890     xhrOnLoad : function(xhr)
32891     {
32892         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32893             el.remove();
32894         }, this);
32895         
32896         if (xhr.readyState !== 4) {
32897             this.arrange();
32898             this.fireEvent('exception', this, xhr);
32899             return;
32900         }
32901
32902         var response = Roo.decode(xhr.responseText);
32903         
32904         if(!response.success){
32905             this.arrange();
32906             this.fireEvent('exception', this, xhr);
32907             return;
32908         }
32909         
32910         var file = this.renderPreview(response.data);
32911         
32912         this.files.push(file);
32913         
32914         this.arrange();
32915         
32916         this.fireEvent('afterupload', this, xhr);
32917         
32918     },
32919     
32920     xhrOnError : function(xhr)
32921     {
32922         Roo.log('xhr on error');
32923         
32924         var response = Roo.decode(xhr.responseText);
32925           
32926         Roo.log(response);
32927         
32928         this.arrange();
32929     },
32930     
32931     process : function(file)
32932     {
32933         if(this.fireEvent('process', this, file) !== false){
32934             if(this.editable && file.type.indexOf('image') != -1){
32935                 this.fireEvent('edit', this, file);
32936                 return;
32937             }
32938
32939             this.uploadStart(file, false);
32940
32941             return;
32942         }
32943         
32944     },
32945     
32946     uploadStart : function(file, crop)
32947     {
32948         this.xhr = new XMLHttpRequest();
32949         
32950         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32951             this.arrange();
32952             return;
32953         }
32954         
32955         file.xhr = this.xhr;
32956             
32957         this.managerEl.createChild({
32958             tag : 'div',
32959             cls : 'roo-document-manager-loading',
32960             cn : [
32961                 {
32962                     tag : 'div',
32963                     tooltip : file.name,
32964                     cls : 'roo-document-manager-thumb',
32965                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32966                 }
32967             ]
32968
32969         });
32970
32971         this.xhr.open(this.method, this.url, true);
32972         
32973         var headers = {
32974             "Accept": "application/json",
32975             "Cache-Control": "no-cache",
32976             "X-Requested-With": "XMLHttpRequest"
32977         };
32978         
32979         for (var headerName in headers) {
32980             var headerValue = headers[headerName];
32981             if (headerValue) {
32982                 this.xhr.setRequestHeader(headerName, headerValue);
32983             }
32984         }
32985         
32986         var _this = this;
32987         
32988         this.xhr.onload = function()
32989         {
32990             _this.xhrOnLoad(_this.xhr);
32991         }
32992         
32993         this.xhr.onerror = function()
32994         {
32995             _this.xhrOnError(_this.xhr);
32996         }
32997         
32998         var formData = new FormData();
32999
33000         formData.append('returnHTML', 'NO');
33001         
33002         if(crop){
33003             formData.append('crop', crop);
33004         }
33005         
33006         formData.append(this.paramName, file, file.name);
33007         
33008         var options = {
33009             file : file, 
33010             manually : false
33011         };
33012         
33013         if(this.fireEvent('prepare', this, formData, options) != false){
33014             
33015             if(options.manually){
33016                 return;
33017             }
33018             
33019             this.xhr.send(formData);
33020             return;
33021         };
33022         
33023         this.uploadCancel();
33024     },
33025     
33026     uploadCancel : function()
33027     {
33028         if (this.xhr) {
33029             this.xhr.abort();
33030         }
33031         
33032         this.delegates = [];
33033         
33034         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33035             el.remove();
33036         }, this);
33037         
33038         this.arrange();
33039     },
33040     
33041     renderPreview : function(file)
33042     {
33043         if(typeof(file.target) != 'undefined' && file.target){
33044             return file;
33045         }
33046         
33047         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33048         
33049         var previewEl = this.managerEl.createChild({
33050             tag : 'div',
33051             cls : 'roo-document-manager-preview',
33052             cn : [
33053                 {
33054                     tag : 'div',
33055                     tooltip : file[this.toolTipName],
33056                     cls : 'roo-document-manager-thumb',
33057                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33058                 },
33059                 {
33060                     tag : 'button',
33061                     cls : 'close',
33062                     html : '<i class="fa fa-times-circle"></i>'
33063                 }
33064             ]
33065         });
33066
33067         var close = previewEl.select('button.close', true).first();
33068
33069         close.on('click', this.onRemove, this, file);
33070
33071         file.target = previewEl;
33072
33073         var image = previewEl.select('img', true).first();
33074         
33075         var _this = this;
33076         
33077         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33078         
33079         image.on('click', this.onClick, this, file);
33080         
33081         this.fireEvent('previewrendered', this, file);
33082         
33083         return file;
33084         
33085     },
33086     
33087     onPreviewLoad : function(file, image)
33088     {
33089         if(typeof(file.target) == 'undefined' || !file.target){
33090             return;
33091         }
33092         
33093         var width = image.dom.naturalWidth || image.dom.width;
33094         var height = image.dom.naturalHeight || image.dom.height;
33095         
33096         if(!this.previewResize) {
33097             return;
33098         }
33099         
33100         if(width > height){
33101             file.target.addClass('wide');
33102             return;
33103         }
33104         
33105         file.target.addClass('tall');
33106         return;
33107         
33108     },
33109     
33110     uploadFromSource : function(file, crop)
33111     {
33112         this.xhr = new XMLHttpRequest();
33113         
33114         this.managerEl.createChild({
33115             tag : 'div',
33116             cls : 'roo-document-manager-loading',
33117             cn : [
33118                 {
33119                     tag : 'div',
33120                     tooltip : file.name,
33121                     cls : 'roo-document-manager-thumb',
33122                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33123                 }
33124             ]
33125
33126         });
33127
33128         this.xhr.open(this.method, this.url, true);
33129         
33130         var headers = {
33131             "Accept": "application/json",
33132             "Cache-Control": "no-cache",
33133             "X-Requested-With": "XMLHttpRequest"
33134         };
33135         
33136         for (var headerName in headers) {
33137             var headerValue = headers[headerName];
33138             if (headerValue) {
33139                 this.xhr.setRequestHeader(headerName, headerValue);
33140             }
33141         }
33142         
33143         var _this = this;
33144         
33145         this.xhr.onload = function()
33146         {
33147             _this.xhrOnLoad(_this.xhr);
33148         }
33149         
33150         this.xhr.onerror = function()
33151         {
33152             _this.xhrOnError(_this.xhr);
33153         }
33154         
33155         var formData = new FormData();
33156
33157         formData.append('returnHTML', 'NO');
33158         
33159         formData.append('crop', crop);
33160         
33161         if(typeof(file.filename) != 'undefined'){
33162             formData.append('filename', file.filename);
33163         }
33164         
33165         if(typeof(file.mimetype) != 'undefined'){
33166             formData.append('mimetype', file.mimetype);
33167         }
33168         
33169         Roo.log(formData);
33170         
33171         if(this.fireEvent('prepare', this, formData) != false){
33172             this.xhr.send(formData);
33173         };
33174     }
33175 });
33176
33177 /*
33178 * Licence: LGPL
33179 */
33180
33181 /**
33182  * @class Roo.bootstrap.DocumentViewer
33183  * @extends Roo.bootstrap.Component
33184  * Bootstrap DocumentViewer class
33185  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33186  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33187  * 
33188  * @constructor
33189  * Create a new DocumentViewer
33190  * @param {Object} config The config object
33191  */
33192
33193 Roo.bootstrap.DocumentViewer = function(config){
33194     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33195     
33196     this.addEvents({
33197         /**
33198          * @event initial
33199          * Fire after initEvent
33200          * @param {Roo.bootstrap.DocumentViewer} this
33201          */
33202         "initial" : true,
33203         /**
33204          * @event click
33205          * Fire after click
33206          * @param {Roo.bootstrap.DocumentViewer} this
33207          */
33208         "click" : true,
33209         /**
33210          * @event download
33211          * Fire after download button
33212          * @param {Roo.bootstrap.DocumentViewer} this
33213          */
33214         "download" : true,
33215         /**
33216          * @event trash
33217          * Fire after trash button
33218          * @param {Roo.bootstrap.DocumentViewer} this
33219          */
33220         "trash" : true
33221         
33222     });
33223 };
33224
33225 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33226     
33227     showDownload : true,
33228     
33229     showTrash : true,
33230     
33231     getAutoCreate : function()
33232     {
33233         var cfg = {
33234             tag : 'div',
33235             cls : 'roo-document-viewer',
33236             cn : [
33237                 {
33238                     tag : 'div',
33239                     cls : 'roo-document-viewer-body',
33240                     cn : [
33241                         {
33242                             tag : 'div',
33243                             cls : 'roo-document-viewer-thumb',
33244                             cn : [
33245                                 {
33246                                     tag : 'img',
33247                                     cls : 'roo-document-viewer-image'
33248                                 }
33249                             ]
33250                         }
33251                     ]
33252                 },
33253                 {
33254                     tag : 'div',
33255                     cls : 'roo-document-viewer-footer',
33256                     cn : {
33257                         tag : 'div',
33258                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33259                         cn : [
33260                             {
33261                                 tag : 'div',
33262                                 cls : 'btn-group roo-document-viewer-download',
33263                                 cn : [
33264                                     {
33265                                         tag : 'button',
33266                                         cls : 'btn btn-default',
33267                                         html : '<i class="fa fa-download"></i>'
33268                                     }
33269                                 ]
33270                             },
33271                             {
33272                                 tag : 'div',
33273                                 cls : 'btn-group roo-document-viewer-trash',
33274                                 cn : [
33275                                     {
33276                                         tag : 'button',
33277                                         cls : 'btn btn-default',
33278                                         html : '<i class="fa fa-trash"></i>'
33279                                     }
33280                                 ]
33281                             }
33282                         ]
33283                     }
33284                 }
33285             ]
33286         };
33287         
33288         return cfg;
33289     },
33290     
33291     initEvents : function()
33292     {
33293         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33294         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33295         
33296         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33297         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33298         
33299         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33300         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33301         
33302         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33303         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33304         
33305         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33306         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33307         
33308         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33309         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33310         
33311         this.bodyEl.on('click', this.onClick, this);
33312         this.downloadBtn.on('click', this.onDownload, this);
33313         this.trashBtn.on('click', this.onTrash, this);
33314         
33315         this.downloadBtn.hide();
33316         this.trashBtn.hide();
33317         
33318         if(this.showDownload){
33319             this.downloadBtn.show();
33320         }
33321         
33322         if(this.showTrash){
33323             this.trashBtn.show();
33324         }
33325         
33326         if(!this.showDownload && !this.showTrash) {
33327             this.footerEl.hide();
33328         }
33329         
33330     },
33331     
33332     initial : function()
33333     {
33334         this.fireEvent('initial', this);
33335         
33336     },
33337     
33338     onClick : function(e)
33339     {
33340         e.preventDefault();
33341         
33342         this.fireEvent('click', this);
33343     },
33344     
33345     onDownload : function(e)
33346     {
33347         e.preventDefault();
33348         
33349         this.fireEvent('download', this);
33350     },
33351     
33352     onTrash : function(e)
33353     {
33354         e.preventDefault();
33355         
33356         this.fireEvent('trash', this);
33357     }
33358     
33359 });
33360 /*
33361  * - LGPL
33362  *
33363  * FieldLabel
33364  * 
33365  */
33366
33367 /**
33368  * @class Roo.bootstrap.form.FieldLabel
33369  * @extends Roo.bootstrap.Component
33370  * Bootstrap FieldLabel class
33371  * @cfg {String} html contents of the element
33372  * @cfg {String} tag tag of the element default label
33373  * @cfg {String} cls class of the element
33374  * @cfg {String} target label target 
33375  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33376  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33377  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33378  * @cfg {String} iconTooltip default "This field is required"
33379  * @cfg {String} indicatorpos (left|right) default left
33380  * 
33381  * @constructor
33382  * Create a new FieldLabel
33383  * @param {Object} config The config object
33384  */
33385
33386 Roo.bootstrap.form.FieldLabel = function(config){
33387     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33388     
33389     this.addEvents({
33390             /**
33391              * @event invalid
33392              * Fires after the field has been marked as invalid.
33393              * @param {Roo.form.FieldLabel} this
33394              * @param {String} msg The validation message
33395              */
33396             invalid : true,
33397             /**
33398              * @event valid
33399              * Fires after the field has been validated with no errors.
33400              * @param {Roo.form.FieldLabel} this
33401              */
33402             valid : true
33403         });
33404 };
33405
33406 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33407     
33408     tag: 'label',
33409     cls: '',
33410     html: '',
33411     target: '',
33412     allowBlank : true,
33413     invalidClass : 'has-warning',
33414     validClass : 'has-success',
33415     iconTooltip : 'This field is required',
33416     indicatorpos : 'left',
33417     
33418     getAutoCreate : function(){
33419         
33420         var cls = "";
33421         if (!this.allowBlank) {
33422             cls  = "visible";
33423         }
33424         
33425         var cfg = {
33426             tag : this.tag,
33427             cls : 'roo-bootstrap-field-label ' + this.cls,
33428             for : this.target,
33429             cn : [
33430                 {
33431                     tag : 'i',
33432                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33433                     tooltip : this.iconTooltip
33434                 },
33435                 {
33436                     tag : 'span',
33437                     html : this.html
33438                 }
33439             ] 
33440         };
33441         
33442         if(this.indicatorpos == 'right'){
33443             var cfg = {
33444                 tag : this.tag,
33445                 cls : 'roo-bootstrap-field-label ' + this.cls,
33446                 for : this.target,
33447                 cn : [
33448                     {
33449                         tag : 'span',
33450                         html : this.html
33451                     },
33452                     {
33453                         tag : 'i',
33454                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33455                         tooltip : this.iconTooltip
33456                     }
33457                 ] 
33458             };
33459         }
33460         
33461         return cfg;
33462     },
33463     
33464     initEvents: function() 
33465     {
33466         Roo.bootstrap.Element.superclass.initEvents.call(this);
33467         
33468         this.indicator = this.indicatorEl();
33469         
33470         if(this.indicator){
33471             this.indicator.removeClass('visible');
33472             this.indicator.addClass('invisible');
33473         }
33474         
33475         Roo.bootstrap.form.FieldLabel.register(this);
33476     },
33477     
33478     indicatorEl : function()
33479     {
33480         var indicator = this.el.select('i.roo-required-indicator',true).first();
33481         
33482         if(!indicator){
33483             return false;
33484         }
33485         
33486         return indicator;
33487         
33488     },
33489     
33490     /**
33491      * Mark this field as valid
33492      */
33493     markValid : function()
33494     {
33495         if(this.indicator){
33496             this.indicator.removeClass('visible');
33497             this.indicator.addClass('invisible');
33498         }
33499         if (Roo.bootstrap.version == 3) {
33500             this.el.removeClass(this.invalidClass);
33501             this.el.addClass(this.validClass);
33502         } else {
33503             this.el.removeClass('is-invalid');
33504             this.el.addClass('is-valid');
33505         }
33506         
33507         
33508         this.fireEvent('valid', this);
33509     },
33510     
33511     /**
33512      * Mark this field as invalid
33513      * @param {String} msg The validation message
33514      */
33515     markInvalid : function(msg)
33516     {
33517         if(this.indicator){
33518             this.indicator.removeClass('invisible');
33519             this.indicator.addClass('visible');
33520         }
33521           if (Roo.bootstrap.version == 3) {
33522             this.el.removeClass(this.validClass);
33523             this.el.addClass(this.invalidClass);
33524         } else {
33525             this.el.removeClass('is-valid');
33526             this.el.addClass('is-invalid');
33527         }
33528         
33529         
33530         this.fireEvent('invalid', this, msg);
33531     }
33532     
33533    
33534 });
33535
33536 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33537     
33538     groups: {},
33539     
33540      /**
33541     * register a FieldLabel Group
33542     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33543     */
33544     register : function(label)
33545     {
33546         if(this.groups.hasOwnProperty(label.target)){
33547             return;
33548         }
33549      
33550         this.groups[label.target] = label;
33551         
33552     },
33553     /**
33554     * fetch a FieldLabel Group based on the target
33555     * @param {string} target
33556     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33557     */
33558     get: function(target) {
33559         if (typeof(this.groups[target]) == 'undefined') {
33560             return false;
33561         }
33562         
33563         return this.groups[target] ;
33564     }
33565 });
33566
33567  
33568
33569  /*
33570  * - LGPL
33571  *
33572  * page DateSplitField.
33573  * 
33574  */
33575
33576
33577 /**
33578  * @class Roo.bootstrap.form.DateSplitField
33579  * @extends Roo.bootstrap.Component
33580  * Bootstrap DateSplitField class
33581  * @cfg {string} fieldLabel - the label associated
33582  * @cfg {Number} labelWidth set the width of label (0-12)
33583  * @cfg {String} labelAlign (top|left)
33584  * @cfg {Boolean} dayAllowBlank (true|false) default false
33585  * @cfg {Boolean} monthAllowBlank (true|false) default false
33586  * @cfg {Boolean} yearAllowBlank (true|false) default false
33587  * @cfg {string} dayPlaceholder 
33588  * @cfg {string} monthPlaceholder
33589  * @cfg {string} yearPlaceholder
33590  * @cfg {string} dayFormat default 'd'
33591  * @cfg {string} monthFormat default 'm'
33592  * @cfg {string} yearFormat default 'Y'
33593  * @cfg {Number} labellg set the width of label (1-12)
33594  * @cfg {Number} labelmd set the width of label (1-12)
33595  * @cfg {Number} labelsm set the width of label (1-12)
33596  * @cfg {Number} labelxs set the width of label (1-12)
33597
33598  *     
33599  * @constructor
33600  * Create a new DateSplitField
33601  * @param {Object} config The config object
33602  */
33603
33604 Roo.bootstrap.form.DateSplitField = function(config){
33605     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33606     
33607     this.addEvents({
33608         // raw events
33609          /**
33610          * @event years
33611          * getting the data of years
33612          * @param {Roo.bootstrap.form.DateSplitField} this
33613          * @param {Object} years
33614          */
33615         "years" : true,
33616         /**
33617          * @event days
33618          * getting the data of days
33619          * @param {Roo.bootstrap.form.DateSplitField} this
33620          * @param {Object} days
33621          */
33622         "days" : true,
33623         /**
33624          * @event invalid
33625          * Fires after the field has been marked as invalid.
33626          * @param {Roo.form.Field} this
33627          * @param {String} msg The validation message
33628          */
33629         invalid : true,
33630        /**
33631          * @event valid
33632          * Fires after the field has been validated with no errors.
33633          * @param {Roo.form.Field} this
33634          */
33635         valid : true
33636     });
33637 };
33638
33639 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33640     
33641     fieldLabel : '',
33642     labelAlign : 'top',
33643     labelWidth : 3,
33644     dayAllowBlank : false,
33645     monthAllowBlank : false,
33646     yearAllowBlank : false,
33647     dayPlaceholder : '',
33648     monthPlaceholder : '',
33649     yearPlaceholder : '',
33650     dayFormat : 'd',
33651     monthFormat : 'm',
33652     yearFormat : 'Y',
33653     isFormField : true,
33654     labellg : 0,
33655     labelmd : 0,
33656     labelsm : 0,
33657     labelxs : 0,
33658     
33659     getAutoCreate : function()
33660     {
33661         var cfg = {
33662             tag : 'div',
33663             cls : 'row roo-date-split-field-group',
33664             cn : [
33665                 {
33666                     tag : 'input',
33667                     type : 'hidden',
33668                     cls : 'form-hidden-field roo-date-split-field-group-value',
33669                     name : this.name
33670                 }
33671             ]
33672         };
33673         
33674         var labelCls = 'col-md-12';
33675         var contentCls = 'col-md-4';
33676         
33677         if(this.fieldLabel){
33678             
33679             var label = {
33680                 tag : 'div',
33681                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33682                 cn : [
33683                     {
33684                         tag : 'label',
33685                         html : this.fieldLabel
33686                     }
33687                 ]
33688             };
33689             
33690             if(this.labelAlign == 'left'){
33691             
33692                 if(this.labelWidth > 12){
33693                     label.style = "width: " + this.labelWidth + 'px';
33694                 }
33695
33696                 if(this.labelWidth < 13 && this.labelmd == 0){
33697                     this.labelmd = this.labelWidth;
33698                 }
33699
33700                 if(this.labellg > 0){
33701                     labelCls = ' col-lg-' + this.labellg;
33702                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33703                 }
33704
33705                 if(this.labelmd > 0){
33706                     labelCls = ' col-md-' + this.labelmd;
33707                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33708                 }
33709
33710                 if(this.labelsm > 0){
33711                     labelCls = ' col-sm-' + this.labelsm;
33712                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33713                 }
33714
33715                 if(this.labelxs > 0){
33716                     labelCls = ' col-xs-' + this.labelxs;
33717                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33718                 }
33719             }
33720             
33721             label.cls += ' ' + labelCls;
33722             
33723             cfg.cn.push(label);
33724         }
33725         
33726         Roo.each(['day', 'month', 'year'], function(t){
33727             cfg.cn.push({
33728                 tag : 'div',
33729                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33730             });
33731         }, this);
33732         
33733         return cfg;
33734     },
33735     
33736     inputEl: function ()
33737     {
33738         return this.el.select('.roo-date-split-field-group-value', true).first();
33739     },
33740     
33741     onRender : function(ct, position) 
33742     {
33743         var _this = this;
33744         
33745         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33746         
33747         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33748         
33749         this.dayField = new Roo.bootstrap.form.ComboBox({
33750             allowBlank : this.dayAllowBlank,
33751             alwaysQuery : true,
33752             displayField : 'value',
33753             editable : false,
33754             fieldLabel : '',
33755             forceSelection : true,
33756             mode : 'local',
33757             placeholder : this.dayPlaceholder,
33758             selectOnFocus : true,
33759             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33760             triggerAction : 'all',
33761             typeAhead : true,
33762             valueField : 'value',
33763             store : new Roo.data.SimpleStore({
33764                 data : (function() {    
33765                     var days = [];
33766                     _this.fireEvent('days', _this, days);
33767                     return days;
33768                 })(),
33769                 fields : [ 'value' ]
33770             }),
33771             listeners : {
33772                 select : function (_self, record, index)
33773                 {
33774                     _this.setValue(_this.getValue());
33775                 }
33776             }
33777         });
33778
33779         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33780         
33781         this.monthField = new Roo.bootstrap.form.MonthField({
33782             after : '<i class=\"fa fa-calendar\"></i>',
33783             allowBlank : this.monthAllowBlank,
33784             placeholder : this.monthPlaceholder,
33785             readOnly : true,
33786             listeners : {
33787                 render : function (_self)
33788                 {
33789                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33790                         e.preventDefault();
33791                         _self.focus();
33792                     });
33793                 },
33794                 select : function (_self, oldvalue, newvalue)
33795                 {
33796                     _this.setValue(_this.getValue());
33797                 }
33798             }
33799         });
33800         
33801         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33802         
33803         this.yearField = new Roo.bootstrap.form.ComboBox({
33804             allowBlank : this.yearAllowBlank,
33805             alwaysQuery : true,
33806             displayField : 'value',
33807             editable : false,
33808             fieldLabel : '',
33809             forceSelection : true,
33810             mode : 'local',
33811             placeholder : this.yearPlaceholder,
33812             selectOnFocus : true,
33813             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33814             triggerAction : 'all',
33815             typeAhead : true,
33816             valueField : 'value',
33817             store : new Roo.data.SimpleStore({
33818                 data : (function() {
33819                     var years = [];
33820                     _this.fireEvent('years', _this, years);
33821                     return years;
33822                 })(),
33823                 fields : [ 'value' ]
33824             }),
33825             listeners : {
33826                 select : function (_self, record, index)
33827                 {
33828                     _this.setValue(_this.getValue());
33829                 }
33830             }
33831         });
33832
33833         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33834     },
33835     
33836     setValue : function(v, format)
33837     {
33838         this.inputEl.dom.value = v;
33839         
33840         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33841         
33842         var d = Date.parseDate(v, f);
33843         
33844         if(!d){
33845             this.validate();
33846             return;
33847         }
33848         
33849         this.setDay(d.format(this.dayFormat));
33850         this.setMonth(d.format(this.monthFormat));
33851         this.setYear(d.format(this.yearFormat));
33852         
33853         this.validate();
33854         
33855         return;
33856     },
33857     
33858     setDay : function(v)
33859     {
33860         this.dayField.setValue(v);
33861         this.inputEl.dom.value = this.getValue();
33862         this.validate();
33863         return;
33864     },
33865     
33866     setMonth : function(v)
33867     {
33868         this.monthField.setValue(v, true);
33869         this.inputEl.dom.value = this.getValue();
33870         this.validate();
33871         return;
33872     },
33873     
33874     setYear : function(v)
33875     {
33876         this.yearField.setValue(v);
33877         this.inputEl.dom.value = this.getValue();
33878         this.validate();
33879         return;
33880     },
33881     
33882     getDay : function()
33883     {
33884         return this.dayField.getValue();
33885     },
33886     
33887     getMonth : function()
33888     {
33889         return this.monthField.getValue();
33890     },
33891     
33892     getYear : function()
33893     {
33894         return this.yearField.getValue();
33895     },
33896     
33897     getValue : function()
33898     {
33899         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33900         
33901         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33902         
33903         return date;
33904     },
33905     
33906     reset : function()
33907     {
33908         this.setDay('');
33909         this.setMonth('');
33910         this.setYear('');
33911         this.inputEl.dom.value = '';
33912         this.validate();
33913         return;
33914     },
33915     
33916     validate : function()
33917     {
33918         var d = this.dayField.validate();
33919         var m = this.monthField.validate();
33920         var y = this.yearField.validate();
33921         
33922         var valid = true;
33923         
33924         if(
33925                 (!this.dayAllowBlank && !d) ||
33926                 (!this.monthAllowBlank && !m) ||
33927                 (!this.yearAllowBlank && !y)
33928         ){
33929             valid = false;
33930         }
33931         
33932         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33933             return valid;
33934         }
33935         
33936         if(valid){
33937             this.markValid();
33938             return valid;
33939         }
33940         
33941         this.markInvalid();
33942         
33943         return valid;
33944     },
33945     
33946     markValid : function()
33947     {
33948         
33949         var label = this.el.select('label', true).first();
33950         var icon = this.el.select('i.fa-star', true).first();
33951
33952         if(label && icon){
33953             icon.remove();
33954         }
33955         
33956         this.fireEvent('valid', this);
33957     },
33958     
33959      /**
33960      * Mark this field as invalid
33961      * @param {String} msg The validation message
33962      */
33963     markInvalid : function(msg)
33964     {
33965         
33966         var label = this.el.select('label', true).first();
33967         var icon = this.el.select('i.fa-star', true).first();
33968
33969         if(label && !icon){
33970             this.el.select('.roo-date-split-field-label', true).createChild({
33971                 tag : 'i',
33972                 cls : 'text-danger fa fa-lg fa-star',
33973                 tooltip : 'This field is required',
33974                 style : 'margin-right:5px;'
33975             }, label, true);
33976         }
33977         
33978         this.fireEvent('invalid', this, msg);
33979     },
33980     
33981     clearInvalid : function()
33982     {
33983         var label = this.el.select('label', true).first();
33984         var icon = this.el.select('i.fa-star', true).first();
33985
33986         if(label && icon){
33987             icon.remove();
33988         }
33989         
33990         this.fireEvent('valid', this);
33991     },
33992     
33993     getName: function()
33994     {
33995         return this.name;
33996     }
33997     
33998 });
33999
34000  
34001
34002 /**
34003  * @class Roo.bootstrap.LayoutMasonry
34004  * @extends Roo.bootstrap.Component
34005  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34006  * Bootstrap Layout Masonry class
34007  *
34008  * This is based on 
34009  * http://masonry.desandro.com
34010  *
34011  * The idea is to render all the bricks based on vertical width...
34012  *
34013  * The original code extends 'outlayer' - we might need to use that....
34014
34015  * @constructor
34016  * Create a new Element
34017  * @param {Object} config The config object
34018  */
34019
34020 Roo.bootstrap.LayoutMasonry = function(config){
34021     
34022     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34023     
34024     this.bricks = [];
34025     
34026     Roo.bootstrap.LayoutMasonry.register(this);
34027     
34028     this.addEvents({
34029         // raw events
34030         /**
34031          * @event layout
34032          * Fire after layout the items
34033          * @param {Roo.bootstrap.LayoutMasonry} this
34034          * @param {Roo.EventObject} e
34035          */
34036         "layout" : true
34037     });
34038     
34039 };
34040
34041 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34042     
34043     /**
34044      * @cfg {Boolean} isLayoutInstant = no animation?
34045      */   
34046     isLayoutInstant : false, // needed?
34047    
34048     /**
34049      * @cfg {Number} boxWidth  width of the columns
34050      */   
34051     boxWidth : 450,
34052     
34053       /**
34054      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34055      */   
34056     boxHeight : 0,
34057     
34058     /**
34059      * @cfg {Number} padWidth padding below box..
34060      */   
34061     padWidth : 10, 
34062     
34063     /**
34064      * @cfg {Number} gutter gutter width..
34065      */   
34066     gutter : 10,
34067     
34068      /**
34069      * @cfg {Number} maxCols maximum number of columns
34070      */   
34071     
34072     maxCols: 0,
34073     
34074     /**
34075      * @cfg {Boolean} isAutoInitial defalut true
34076      */   
34077     isAutoInitial : true, 
34078     
34079     containerWidth: 0,
34080     
34081     /**
34082      * @cfg {Boolean} isHorizontal defalut false
34083      */   
34084     isHorizontal : false, 
34085
34086     currentSize : null,
34087     
34088     tag: 'div',
34089     
34090     cls: '',
34091     
34092     bricks: null, //CompositeElement
34093     
34094     cols : 1,
34095     
34096     _isLayoutInited : false,
34097     
34098 //    isAlternative : false, // only use for vertical layout...
34099     
34100     /**
34101      * @cfg {Number} alternativePadWidth padding below box..
34102      */   
34103     alternativePadWidth : 50,
34104     
34105     selectedBrick : [],
34106     
34107     getAutoCreate : function(){
34108         
34109         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34110         
34111         var cfg = {
34112             tag: this.tag,
34113             cls: 'blog-masonary-wrapper ' + this.cls,
34114             cn : {
34115                 cls : 'mas-boxes masonary'
34116             }
34117         };
34118         
34119         return cfg;
34120     },
34121     
34122     getChildContainer: function( )
34123     {
34124         if (this.boxesEl) {
34125             return this.boxesEl;
34126         }
34127         
34128         this.boxesEl = this.el.select('.mas-boxes').first();
34129         
34130         return this.boxesEl;
34131     },
34132     
34133     
34134     initEvents : function()
34135     {
34136         var _this = this;
34137         
34138         if(this.isAutoInitial){
34139             Roo.log('hook children rendered');
34140             this.on('childrenrendered', function() {
34141                 Roo.log('children rendered');
34142                 _this.initial();
34143             } ,this);
34144         }
34145     },
34146     
34147     initial : function()
34148     {
34149         this.selectedBrick = [];
34150         
34151         this.currentSize = this.el.getBox(true);
34152         
34153         Roo.EventManager.onWindowResize(this.resize, this); 
34154
34155         if(!this.isAutoInitial){
34156             this.layout();
34157             return;
34158         }
34159         
34160         this.layout();
34161         
34162         return;
34163         //this.layout.defer(500,this);
34164         
34165     },
34166     
34167     resize : function()
34168     {
34169         var cs = this.el.getBox(true);
34170         
34171         if (
34172                 this.currentSize.width == cs.width && 
34173                 this.currentSize.x == cs.x && 
34174                 this.currentSize.height == cs.height && 
34175                 this.currentSize.y == cs.y 
34176         ) {
34177             Roo.log("no change in with or X or Y");
34178             return;
34179         }
34180         
34181         this.currentSize = cs;
34182         
34183         this.layout();
34184         
34185     },
34186     
34187     layout : function()
34188     {   
34189         this._resetLayout();
34190         
34191         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34192         
34193         this.layoutItems( isInstant );
34194       
34195         this._isLayoutInited = true;
34196         
34197         this.fireEvent('layout', this);
34198         
34199     },
34200     
34201     _resetLayout : function()
34202     {
34203         if(this.isHorizontal){
34204             this.horizontalMeasureColumns();
34205             return;
34206         }
34207         
34208         this.verticalMeasureColumns();
34209         
34210     },
34211     
34212     verticalMeasureColumns : function()
34213     {
34214         this.getContainerWidth();
34215         
34216 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34217 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34218 //            return;
34219 //        }
34220         
34221         var boxWidth = this.boxWidth + this.padWidth;
34222         
34223         if(this.containerWidth < this.boxWidth){
34224             boxWidth = this.containerWidth
34225         }
34226         
34227         var containerWidth = this.containerWidth;
34228         
34229         var cols = Math.floor(containerWidth / boxWidth);
34230         
34231         this.cols = Math.max( cols, 1 );
34232         
34233         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34234         
34235         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34236         
34237         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34238         
34239         this.colWidth = boxWidth + avail - this.padWidth;
34240         
34241         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34242         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34243     },
34244     
34245     horizontalMeasureColumns : function()
34246     {
34247         this.getContainerWidth();
34248         
34249         var boxWidth = this.boxWidth;
34250         
34251         if(this.containerWidth < boxWidth){
34252             boxWidth = this.containerWidth;
34253         }
34254         
34255         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34256         
34257         this.el.setHeight(boxWidth);
34258         
34259     },
34260     
34261     getContainerWidth : function()
34262     {
34263         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34264     },
34265     
34266     layoutItems : function( isInstant )
34267     {
34268         Roo.log(this.bricks);
34269         
34270         var items = Roo.apply([], this.bricks);
34271         
34272         if(this.isHorizontal){
34273             this._horizontalLayoutItems( items , isInstant );
34274             return;
34275         }
34276         
34277 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34278 //            this._verticalAlternativeLayoutItems( items , isInstant );
34279 //            return;
34280 //        }
34281         
34282         this._verticalLayoutItems( items , isInstant );
34283         
34284     },
34285     
34286     _verticalLayoutItems : function ( items , isInstant)
34287     {
34288         if ( !items || !items.length ) {
34289             return;
34290         }
34291         
34292         var standard = [
34293             ['xs', 'xs', 'xs', 'tall'],
34294             ['xs', 'xs', 'tall'],
34295             ['xs', 'xs', 'sm'],
34296             ['xs', 'xs', 'xs'],
34297             ['xs', 'tall'],
34298             ['xs', 'sm'],
34299             ['xs', 'xs'],
34300             ['xs'],
34301             
34302             ['sm', 'xs', 'xs'],
34303             ['sm', 'xs'],
34304             ['sm'],
34305             
34306             ['tall', 'xs', 'xs', 'xs'],
34307             ['tall', 'xs', 'xs'],
34308             ['tall', 'xs'],
34309             ['tall']
34310             
34311         ];
34312         
34313         var queue = [];
34314         
34315         var boxes = [];
34316         
34317         var box = [];
34318         
34319         Roo.each(items, function(item, k){
34320             
34321             switch (item.size) {
34322                 // these layouts take up a full box,
34323                 case 'md' :
34324                 case 'md-left' :
34325                 case 'md-right' :
34326                 case 'wide' :
34327                     
34328                     if(box.length){
34329                         boxes.push(box);
34330                         box = [];
34331                     }
34332                     
34333                     boxes.push([item]);
34334                     
34335                     break;
34336                     
34337                 case 'xs' :
34338                 case 'sm' :
34339                 case 'tall' :
34340                     
34341                     box.push(item);
34342                     
34343                     break;
34344                 default :
34345                     break;
34346                     
34347             }
34348             
34349         }, this);
34350         
34351         if(box.length){
34352             boxes.push(box);
34353             box = [];
34354         }
34355         
34356         var filterPattern = function(box, length)
34357         {
34358             if(!box.length){
34359                 return;
34360             }
34361             
34362             var match = false;
34363             
34364             var pattern = box.slice(0, length);
34365             
34366             var format = [];
34367             
34368             Roo.each(pattern, function(i){
34369                 format.push(i.size);
34370             }, this);
34371             
34372             Roo.each(standard, function(s){
34373                 
34374                 if(String(s) != String(format)){
34375                     return;
34376                 }
34377                 
34378                 match = true;
34379                 return false;
34380                 
34381             }, this);
34382             
34383             if(!match && length == 1){
34384                 return;
34385             }
34386             
34387             if(!match){
34388                 filterPattern(box, length - 1);
34389                 return;
34390             }
34391                 
34392             queue.push(pattern);
34393
34394             box = box.slice(length, box.length);
34395
34396             filterPattern(box, 4);
34397
34398             return;
34399             
34400         }
34401         
34402         Roo.each(boxes, function(box, k){
34403             
34404             if(!box.length){
34405                 return;
34406             }
34407             
34408             if(box.length == 1){
34409                 queue.push(box);
34410                 return;
34411             }
34412             
34413             filterPattern(box, 4);
34414             
34415         }, this);
34416         
34417         this._processVerticalLayoutQueue( queue, isInstant );
34418         
34419     },
34420     
34421 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34422 //    {
34423 //        if ( !items || !items.length ) {
34424 //            return;
34425 //        }
34426 //
34427 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34428 //        
34429 //    },
34430     
34431     _horizontalLayoutItems : function ( items , isInstant)
34432     {
34433         if ( !items || !items.length || items.length < 3) {
34434             return;
34435         }
34436         
34437         items.reverse();
34438         
34439         var eItems = items.slice(0, 3);
34440         
34441         items = items.slice(3, items.length);
34442         
34443         var standard = [
34444             ['xs', 'xs', 'xs', 'wide'],
34445             ['xs', 'xs', 'wide'],
34446             ['xs', 'xs', 'sm'],
34447             ['xs', 'xs', 'xs'],
34448             ['xs', 'wide'],
34449             ['xs', 'sm'],
34450             ['xs', 'xs'],
34451             ['xs'],
34452             
34453             ['sm', 'xs', 'xs'],
34454             ['sm', 'xs'],
34455             ['sm'],
34456             
34457             ['wide', 'xs', 'xs', 'xs'],
34458             ['wide', 'xs', 'xs'],
34459             ['wide', 'xs'],
34460             ['wide'],
34461             
34462             ['wide-thin']
34463         ];
34464         
34465         var queue = [];
34466         
34467         var boxes = [];
34468         
34469         var box = [];
34470         
34471         Roo.each(items, function(item, k){
34472             
34473             switch (item.size) {
34474                 case 'md' :
34475                 case 'md-left' :
34476                 case 'md-right' :
34477                 case 'tall' :
34478                     
34479                     if(box.length){
34480                         boxes.push(box);
34481                         box = [];
34482                     }
34483                     
34484                     boxes.push([item]);
34485                     
34486                     break;
34487                     
34488                 case 'xs' :
34489                 case 'sm' :
34490                 case 'wide' :
34491                 case 'wide-thin' :
34492                     
34493                     box.push(item);
34494                     
34495                     break;
34496                 default :
34497                     break;
34498                     
34499             }
34500             
34501         }, this);
34502         
34503         if(box.length){
34504             boxes.push(box);
34505             box = [];
34506         }
34507         
34508         var filterPattern = function(box, length)
34509         {
34510             if(!box.length){
34511                 return;
34512             }
34513             
34514             var match = false;
34515             
34516             var pattern = box.slice(0, length);
34517             
34518             var format = [];
34519             
34520             Roo.each(pattern, function(i){
34521                 format.push(i.size);
34522             }, this);
34523             
34524             Roo.each(standard, function(s){
34525                 
34526                 if(String(s) != String(format)){
34527                     return;
34528                 }
34529                 
34530                 match = true;
34531                 return false;
34532                 
34533             }, this);
34534             
34535             if(!match && length == 1){
34536                 return;
34537             }
34538             
34539             if(!match){
34540                 filterPattern(box, length - 1);
34541                 return;
34542             }
34543                 
34544             queue.push(pattern);
34545
34546             box = box.slice(length, box.length);
34547
34548             filterPattern(box, 4);
34549
34550             return;
34551             
34552         }
34553         
34554         Roo.each(boxes, function(box, k){
34555             
34556             if(!box.length){
34557                 return;
34558             }
34559             
34560             if(box.length == 1){
34561                 queue.push(box);
34562                 return;
34563             }
34564             
34565             filterPattern(box, 4);
34566             
34567         }, this);
34568         
34569         
34570         var prune = [];
34571         
34572         var pos = this.el.getBox(true);
34573         
34574         var minX = pos.x;
34575         
34576         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34577         
34578         var hit_end = false;
34579         
34580         Roo.each(queue, function(box){
34581             
34582             if(hit_end){
34583                 
34584                 Roo.each(box, function(b){
34585                 
34586                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34587                     b.el.hide();
34588
34589                 }, this);
34590
34591                 return;
34592             }
34593             
34594             var mx = 0;
34595             
34596             Roo.each(box, function(b){
34597                 
34598                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34599                 b.el.show();
34600
34601                 mx = Math.max(mx, b.x);
34602                 
34603             }, this);
34604             
34605             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34606             
34607             if(maxX < minX){
34608                 
34609                 Roo.each(box, function(b){
34610                 
34611                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34612                     b.el.hide();
34613                     
34614                 }, this);
34615                 
34616                 hit_end = true;
34617                 
34618                 return;
34619             }
34620             
34621             prune.push(box);
34622             
34623         }, this);
34624         
34625         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34626     },
34627     
34628     /** Sets position of item in DOM
34629     * @param {Element} item
34630     * @param {Number} x - horizontal position
34631     * @param {Number} y - vertical position
34632     * @param {Boolean} isInstant - disables transitions
34633     */
34634     _processVerticalLayoutQueue : function( queue, isInstant )
34635     {
34636         var pos = this.el.getBox(true);
34637         var x = pos.x;
34638         var y = pos.y;
34639         var maxY = [];
34640         
34641         for (var i = 0; i < this.cols; i++){
34642             maxY[i] = pos.y;
34643         }
34644         
34645         Roo.each(queue, function(box, k){
34646             
34647             var col = k % this.cols;
34648             
34649             Roo.each(box, function(b,kk){
34650                 
34651                 b.el.position('absolute');
34652                 
34653                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34654                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34655                 
34656                 if(b.size == 'md-left' || b.size == 'md-right'){
34657                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34658                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34659                 }
34660                 
34661                 b.el.setWidth(width);
34662                 b.el.setHeight(height);
34663                 // iframe?
34664                 b.el.select('iframe',true).setSize(width,height);
34665                 
34666             }, this);
34667             
34668             for (var i = 0; i < this.cols; i++){
34669                 
34670                 if(maxY[i] < maxY[col]){
34671                     col = i;
34672                     continue;
34673                 }
34674                 
34675                 col = Math.min(col, i);
34676                 
34677             }
34678             
34679             x = pos.x + col * (this.colWidth + this.padWidth);
34680             
34681             y = maxY[col];
34682             
34683             var positions = [];
34684             
34685             switch (box.length){
34686                 case 1 :
34687                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34688                     break;
34689                 case 2 :
34690                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34691                     break;
34692                 case 3 :
34693                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34694                     break;
34695                 case 4 :
34696                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34697                     break;
34698                 default :
34699                     break;
34700             }
34701             
34702             Roo.each(box, function(b,kk){
34703                 
34704                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34705                 
34706                 var sz = b.el.getSize();
34707                 
34708                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34709                 
34710             }, this);
34711             
34712         }, this);
34713         
34714         var mY = 0;
34715         
34716         for (var i = 0; i < this.cols; i++){
34717             mY = Math.max(mY, maxY[i]);
34718         }
34719         
34720         this.el.setHeight(mY - pos.y);
34721         
34722     },
34723     
34724 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34725 //    {
34726 //        var pos = this.el.getBox(true);
34727 //        var x = pos.x;
34728 //        var y = pos.y;
34729 //        var maxX = pos.right;
34730 //        
34731 //        var maxHeight = 0;
34732 //        
34733 //        Roo.each(items, function(item, k){
34734 //            
34735 //            var c = k % 2;
34736 //            
34737 //            item.el.position('absolute');
34738 //                
34739 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34740 //
34741 //            item.el.setWidth(width);
34742 //
34743 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34744 //
34745 //            item.el.setHeight(height);
34746 //            
34747 //            if(c == 0){
34748 //                item.el.setXY([x, y], isInstant ? false : true);
34749 //            } else {
34750 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34751 //            }
34752 //            
34753 //            y = y + height + this.alternativePadWidth;
34754 //            
34755 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34756 //            
34757 //        }, this);
34758 //        
34759 //        this.el.setHeight(maxHeight);
34760 //        
34761 //    },
34762     
34763     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34764     {
34765         var pos = this.el.getBox(true);
34766         
34767         var minX = pos.x;
34768         var minY = pos.y;
34769         
34770         var maxX = pos.right;
34771         
34772         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34773         
34774         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34775         
34776         Roo.each(queue, function(box, k){
34777             
34778             Roo.each(box, function(b, kk){
34779                 
34780                 b.el.position('absolute');
34781                 
34782                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34783                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34784                 
34785                 if(b.size == 'md-left' || b.size == 'md-right'){
34786                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34787                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34788                 }
34789                 
34790                 b.el.setWidth(width);
34791                 b.el.setHeight(height);
34792                 
34793             }, this);
34794             
34795             if(!box.length){
34796                 return;
34797             }
34798             
34799             var positions = [];
34800             
34801             switch (box.length){
34802                 case 1 :
34803                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34804                     break;
34805                 case 2 :
34806                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34807                     break;
34808                 case 3 :
34809                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34810                     break;
34811                 case 4 :
34812                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34813                     break;
34814                 default :
34815                     break;
34816             }
34817             
34818             Roo.each(box, function(b,kk){
34819                 
34820                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34821                 
34822                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34823                 
34824             }, this);
34825             
34826         }, this);
34827         
34828     },
34829     
34830     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34831     {
34832         Roo.each(eItems, function(b,k){
34833             
34834             b.size = (k == 0) ? 'sm' : 'xs';
34835             b.x = (k == 0) ? 2 : 1;
34836             b.y = (k == 0) ? 2 : 1;
34837             
34838             b.el.position('absolute');
34839             
34840             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34841                 
34842             b.el.setWidth(width);
34843             
34844             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34845             
34846             b.el.setHeight(height);
34847             
34848         }, this);
34849
34850         var positions = [];
34851         
34852         positions.push({
34853             x : maxX - this.unitWidth * 2 - this.gutter,
34854             y : minY
34855         });
34856         
34857         positions.push({
34858             x : maxX - this.unitWidth,
34859             y : minY + (this.unitWidth + this.gutter) * 2
34860         });
34861         
34862         positions.push({
34863             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34864             y : minY
34865         });
34866         
34867         Roo.each(eItems, function(b,k){
34868             
34869             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34870
34871         }, this);
34872         
34873     },
34874     
34875     getVerticalOneBoxColPositions : function(x, y, box)
34876     {
34877         var pos = [];
34878         
34879         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34880         
34881         if(box[0].size == 'md-left'){
34882             rand = 0;
34883         }
34884         
34885         if(box[0].size == 'md-right'){
34886             rand = 1;
34887         }
34888         
34889         pos.push({
34890             x : x + (this.unitWidth + this.gutter) * rand,
34891             y : y
34892         });
34893         
34894         return pos;
34895     },
34896     
34897     getVerticalTwoBoxColPositions : function(x, y, box)
34898     {
34899         var pos = [];
34900         
34901         if(box[0].size == 'xs'){
34902             
34903             pos.push({
34904                 x : x,
34905                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34906             });
34907
34908             pos.push({
34909                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34910                 y : y
34911             });
34912             
34913             return pos;
34914             
34915         }
34916         
34917         pos.push({
34918             x : x,
34919             y : y
34920         });
34921
34922         pos.push({
34923             x : x + (this.unitWidth + this.gutter) * 2,
34924             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34925         });
34926         
34927         return pos;
34928         
34929     },
34930     
34931     getVerticalThreeBoxColPositions : function(x, y, box)
34932     {
34933         var pos = [];
34934         
34935         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34936             
34937             pos.push({
34938                 x : x,
34939                 y : y
34940             });
34941
34942             pos.push({
34943                 x : x + (this.unitWidth + this.gutter) * 1,
34944                 y : y
34945             });
34946             
34947             pos.push({
34948                 x : x + (this.unitWidth + this.gutter) * 2,
34949                 y : y
34950             });
34951             
34952             return pos;
34953             
34954         }
34955         
34956         if(box[0].size == 'xs' && box[1].size == 'xs'){
34957             
34958             pos.push({
34959                 x : x,
34960                 y : y
34961             });
34962
34963             pos.push({
34964                 x : x,
34965                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34966             });
34967             
34968             pos.push({
34969                 x : x + (this.unitWidth + this.gutter) * 1,
34970                 y : y
34971             });
34972             
34973             return pos;
34974             
34975         }
34976         
34977         pos.push({
34978             x : x,
34979             y : y
34980         });
34981
34982         pos.push({
34983             x : x + (this.unitWidth + this.gutter) * 2,
34984             y : y
34985         });
34986
34987         pos.push({
34988             x : x + (this.unitWidth + this.gutter) * 2,
34989             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34990         });
34991             
34992         return pos;
34993         
34994     },
34995     
34996     getVerticalFourBoxColPositions : function(x, y, box)
34997     {
34998         var pos = [];
34999         
35000         if(box[0].size == 'xs'){
35001             
35002             pos.push({
35003                 x : x,
35004                 y : y
35005             });
35006
35007             pos.push({
35008                 x : x,
35009                 y : y + (this.unitHeight + this.gutter) * 1
35010             });
35011             
35012             pos.push({
35013                 x : x,
35014                 y : y + (this.unitHeight + this.gutter) * 2
35015             });
35016             
35017             pos.push({
35018                 x : x + (this.unitWidth + this.gutter) * 1,
35019                 y : y
35020             });
35021             
35022             return pos;
35023             
35024         }
35025         
35026         pos.push({
35027             x : x,
35028             y : y
35029         });
35030
35031         pos.push({
35032             x : x + (this.unitWidth + this.gutter) * 2,
35033             y : y
35034         });
35035
35036         pos.push({
35037             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35038             y : y + (this.unitHeight + this.gutter) * 1
35039         });
35040
35041         pos.push({
35042             x : x + (this.unitWidth + this.gutter) * 2,
35043             y : y + (this.unitWidth + this.gutter) * 2
35044         });
35045
35046         return pos;
35047         
35048     },
35049     
35050     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35051     {
35052         var pos = [];
35053         
35054         if(box[0].size == 'md-left'){
35055             pos.push({
35056                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35057                 y : minY
35058             });
35059             
35060             return pos;
35061         }
35062         
35063         if(box[0].size == 'md-right'){
35064             pos.push({
35065                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35066                 y : minY + (this.unitWidth + this.gutter) * 1
35067             });
35068             
35069             return pos;
35070         }
35071         
35072         var rand = Math.floor(Math.random() * (4 - box[0].y));
35073         
35074         pos.push({
35075             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35076             y : minY + (this.unitWidth + this.gutter) * rand
35077         });
35078         
35079         return pos;
35080         
35081     },
35082     
35083     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35084     {
35085         var pos = [];
35086         
35087         if(box[0].size == 'xs'){
35088             
35089             pos.push({
35090                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35091                 y : minY
35092             });
35093
35094             pos.push({
35095                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35096                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35097             });
35098             
35099             return pos;
35100             
35101         }
35102         
35103         pos.push({
35104             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35105             y : minY
35106         });
35107
35108         pos.push({
35109             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35110             y : minY + (this.unitWidth + this.gutter) * 2
35111         });
35112         
35113         return pos;
35114         
35115     },
35116     
35117     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35118     {
35119         var pos = [];
35120         
35121         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35122             
35123             pos.push({
35124                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35125                 y : minY
35126             });
35127
35128             pos.push({
35129                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35130                 y : minY + (this.unitWidth + this.gutter) * 1
35131             });
35132             
35133             pos.push({
35134                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35135                 y : minY + (this.unitWidth + this.gutter) * 2
35136             });
35137             
35138             return pos;
35139             
35140         }
35141         
35142         if(box[0].size == 'xs' && box[1].size == 'xs'){
35143             
35144             pos.push({
35145                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35146                 y : minY
35147             });
35148
35149             pos.push({
35150                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35151                 y : minY
35152             });
35153             
35154             pos.push({
35155                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35156                 y : minY + (this.unitWidth + this.gutter) * 1
35157             });
35158             
35159             return pos;
35160             
35161         }
35162         
35163         pos.push({
35164             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35165             y : minY
35166         });
35167
35168         pos.push({
35169             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35170             y : minY + (this.unitWidth + this.gutter) * 2
35171         });
35172
35173         pos.push({
35174             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35175             y : minY + (this.unitWidth + this.gutter) * 2
35176         });
35177             
35178         return pos;
35179         
35180     },
35181     
35182     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35183     {
35184         var pos = [];
35185         
35186         if(box[0].size == 'xs'){
35187             
35188             pos.push({
35189                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35190                 y : minY
35191             });
35192
35193             pos.push({
35194                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35195                 y : minY
35196             });
35197             
35198             pos.push({
35199                 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),
35200                 y : minY
35201             });
35202             
35203             pos.push({
35204                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35205                 y : minY + (this.unitWidth + this.gutter) * 1
35206             });
35207             
35208             return pos;
35209             
35210         }
35211         
35212         pos.push({
35213             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35214             y : minY
35215         });
35216         
35217         pos.push({
35218             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35219             y : minY + (this.unitWidth + this.gutter) * 2
35220         });
35221         
35222         pos.push({
35223             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35224             y : minY + (this.unitWidth + this.gutter) * 2
35225         });
35226         
35227         pos.push({
35228             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),
35229             y : minY + (this.unitWidth + this.gutter) * 2
35230         });
35231
35232         return pos;
35233         
35234     },
35235     
35236     /**
35237     * remove a Masonry Brick
35238     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35239     */
35240     removeBrick : function(brick_id)
35241     {
35242         if (!brick_id) {
35243             return;
35244         }
35245         
35246         for (var i = 0; i<this.bricks.length; i++) {
35247             if (this.bricks[i].id == brick_id) {
35248                 this.bricks.splice(i,1);
35249                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35250                 this.initial();
35251             }
35252         }
35253     },
35254     
35255     /**
35256     * adds a Masonry Brick
35257     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35258     */
35259     addBrick : function(cfg)
35260     {
35261         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35262         //this.register(cn);
35263         cn.parentId = this.id;
35264         cn.render(this.el);
35265         return cn;
35266     },
35267     
35268     /**
35269     * register a Masonry Brick
35270     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35271     */
35272     
35273     register : function(brick)
35274     {
35275         this.bricks.push(brick);
35276         brick.masonryId = this.id;
35277     },
35278     
35279     /**
35280     * clear all the Masonry Brick
35281     */
35282     clearAll : function()
35283     {
35284         this.bricks = [];
35285         //this.getChildContainer().dom.innerHTML = "";
35286         this.el.dom.innerHTML = '';
35287     },
35288     
35289     getSelected : function()
35290     {
35291         if (!this.selectedBrick) {
35292             return false;
35293         }
35294         
35295         return this.selectedBrick;
35296     }
35297 });
35298
35299 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35300     
35301     groups: {},
35302      /**
35303     * register a Masonry Layout
35304     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35305     */
35306     
35307     register : function(layout)
35308     {
35309         this.groups[layout.id] = layout;
35310     },
35311     /**
35312     * fetch a  Masonry Layout based on the masonry layout ID
35313     * @param {string} the masonry layout to add
35314     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35315     */
35316     
35317     get: function(layout_id) {
35318         if (typeof(this.groups[layout_id]) == 'undefined') {
35319             return false;
35320         }
35321         return this.groups[layout_id] ;
35322     }
35323     
35324     
35325     
35326 });
35327
35328  
35329
35330  /**
35331  *
35332  * This is based on 
35333  * http://masonry.desandro.com
35334  *
35335  * The idea is to render all the bricks based on vertical width...
35336  *
35337  * The original code extends 'outlayer' - we might need to use that....
35338  * 
35339  */
35340
35341
35342 /**
35343  * @class Roo.bootstrap.LayoutMasonryAuto
35344  * @extends Roo.bootstrap.Component
35345  * Bootstrap Layout Masonry class
35346  * 
35347  * @constructor
35348  * Create a new Element
35349  * @param {Object} config The config object
35350  */
35351
35352 Roo.bootstrap.LayoutMasonryAuto = function(config){
35353     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35354 };
35355
35356 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35357     
35358       /**
35359      * @cfg {Boolean} isFitWidth  - resize the width..
35360      */   
35361     isFitWidth : false,  // options..
35362     /**
35363      * @cfg {Boolean} isOriginLeft = left align?
35364      */   
35365     isOriginLeft : true,
35366     /**
35367      * @cfg {Boolean} isOriginTop = top align?
35368      */   
35369     isOriginTop : false,
35370     /**
35371      * @cfg {Boolean} isLayoutInstant = no animation?
35372      */   
35373     isLayoutInstant : false, // needed?
35374     /**
35375      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35376      */   
35377     isResizingContainer : true,
35378     /**
35379      * @cfg {Number} columnWidth  width of the columns 
35380      */   
35381     
35382     columnWidth : 0,
35383     
35384     /**
35385      * @cfg {Number} maxCols maximum number of columns
35386      */   
35387     
35388     maxCols: 0,
35389     /**
35390      * @cfg {Number} padHeight padding below box..
35391      */   
35392     
35393     padHeight : 10, 
35394     
35395     /**
35396      * @cfg {Boolean} isAutoInitial defalut true
35397      */   
35398     
35399     isAutoInitial : true, 
35400     
35401     // private?
35402     gutter : 0,
35403     
35404     containerWidth: 0,
35405     initialColumnWidth : 0,
35406     currentSize : null,
35407     
35408     colYs : null, // array.
35409     maxY : 0,
35410     padWidth: 10,
35411     
35412     
35413     tag: 'div',
35414     cls: '',
35415     bricks: null, //CompositeElement
35416     cols : 0, // array?
35417     // element : null, // wrapped now this.el
35418     _isLayoutInited : null, 
35419     
35420     
35421     getAutoCreate : function(){
35422         
35423         var cfg = {
35424             tag: this.tag,
35425             cls: 'blog-masonary-wrapper ' + this.cls,
35426             cn : {
35427                 cls : 'mas-boxes masonary'
35428             }
35429         };
35430         
35431         return cfg;
35432     },
35433     
35434     getChildContainer: function( )
35435     {
35436         if (this.boxesEl) {
35437             return this.boxesEl;
35438         }
35439         
35440         this.boxesEl = this.el.select('.mas-boxes').first();
35441         
35442         return this.boxesEl;
35443     },
35444     
35445     
35446     initEvents : function()
35447     {
35448         var _this = this;
35449         
35450         if(this.isAutoInitial){
35451             Roo.log('hook children rendered');
35452             this.on('childrenrendered', function() {
35453                 Roo.log('children rendered');
35454                 _this.initial();
35455             } ,this);
35456         }
35457         
35458     },
35459     
35460     initial : function()
35461     {
35462         this.reloadItems();
35463
35464         this.currentSize = this.el.getBox(true);
35465
35466         /// was window resize... - let's see if this works..
35467         Roo.EventManager.onWindowResize(this.resize, this); 
35468
35469         if(!this.isAutoInitial){
35470             this.layout();
35471             return;
35472         }
35473         
35474         this.layout.defer(500,this);
35475     },
35476     
35477     reloadItems: function()
35478     {
35479         this.bricks = this.el.select('.masonry-brick', true);
35480         
35481         this.bricks.each(function(b) {
35482             //Roo.log(b.getSize());
35483             if (!b.attr('originalwidth')) {
35484                 b.attr('originalwidth',  b.getSize().width);
35485             }
35486             
35487         });
35488         
35489         Roo.log(this.bricks.elements.length);
35490     },
35491     
35492     resize : function()
35493     {
35494         Roo.log('resize');
35495         var cs = this.el.getBox(true);
35496         
35497         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35498             Roo.log("no change in with or X");
35499             return;
35500         }
35501         this.currentSize = cs;
35502         this.layout();
35503     },
35504     
35505     layout : function()
35506     {
35507          Roo.log('layout');
35508         this._resetLayout();
35509         //this._manageStamps();
35510       
35511         // don't animate first layout
35512         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35513         this.layoutItems( isInstant );
35514       
35515         // flag for initalized
35516         this._isLayoutInited = true;
35517     },
35518     
35519     layoutItems : function( isInstant )
35520     {
35521         //var items = this._getItemsForLayout( this.items );
35522         // original code supports filtering layout items.. we just ignore it..
35523         
35524         this._layoutItems( this.bricks , isInstant );
35525       
35526         this._postLayout();
35527     },
35528     _layoutItems : function ( items , isInstant)
35529     {
35530        //this.fireEvent( 'layout', this, items );
35531     
35532
35533         if ( !items || !items.elements.length ) {
35534           // no items, emit event with empty array
35535             return;
35536         }
35537
35538         var queue = [];
35539         items.each(function(item) {
35540             Roo.log("layout item");
35541             Roo.log(item);
35542             // get x/y object from method
35543             var position = this._getItemLayoutPosition( item );
35544             // enqueue
35545             position.item = item;
35546             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35547             queue.push( position );
35548         }, this);
35549       
35550         this._processLayoutQueue( queue );
35551     },
35552     /** Sets position of item in DOM
35553     * @param {Element} item
35554     * @param {Number} x - horizontal position
35555     * @param {Number} y - vertical position
35556     * @param {Boolean} isInstant - disables transitions
35557     */
35558     _processLayoutQueue : function( queue )
35559     {
35560         for ( var i=0, len = queue.length; i < len; i++ ) {
35561             var obj = queue[i];
35562             obj.item.position('absolute');
35563             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35564         }
35565     },
35566       
35567     
35568     /**
35569     * Any logic you want to do after each layout,
35570     * i.e. size the container
35571     */
35572     _postLayout : function()
35573     {
35574         this.resizeContainer();
35575     },
35576     
35577     resizeContainer : function()
35578     {
35579         if ( !this.isResizingContainer ) {
35580             return;
35581         }
35582         var size = this._getContainerSize();
35583         if ( size ) {
35584             this.el.setSize(size.width,size.height);
35585             this.boxesEl.setSize(size.width,size.height);
35586         }
35587     },
35588     
35589     
35590     
35591     _resetLayout : function()
35592     {
35593         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35594         this.colWidth = this.el.getWidth();
35595         //this.gutter = this.el.getWidth(); 
35596         
35597         this.measureColumns();
35598
35599         // reset column Y
35600         var i = this.cols;
35601         this.colYs = [];
35602         while (i--) {
35603             this.colYs.push( 0 );
35604         }
35605     
35606         this.maxY = 0;
35607     },
35608
35609     measureColumns : function()
35610     {
35611         this.getContainerWidth();
35612       // if columnWidth is 0, default to outerWidth of first item
35613         if ( !this.columnWidth ) {
35614             var firstItem = this.bricks.first();
35615             Roo.log(firstItem);
35616             this.columnWidth  = this.containerWidth;
35617             if (firstItem && firstItem.attr('originalwidth') ) {
35618                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35619             }
35620             // columnWidth fall back to item of first element
35621             Roo.log("set column width?");
35622                         this.initialColumnWidth = this.columnWidth  ;
35623
35624             // if first elem has no width, default to size of container
35625             
35626         }
35627         
35628         
35629         if (this.initialColumnWidth) {
35630             this.columnWidth = this.initialColumnWidth;
35631         }
35632         
35633         
35634             
35635         // column width is fixed at the top - however if container width get's smaller we should
35636         // reduce it...
35637         
35638         // this bit calcs how man columns..
35639             
35640         var columnWidth = this.columnWidth += this.gutter;
35641       
35642         // calculate columns
35643         var containerWidth = this.containerWidth + this.gutter;
35644         
35645         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35646         // fix rounding errors, typically with gutters
35647         var excess = columnWidth - containerWidth % columnWidth;
35648         
35649         
35650         // if overshoot is less than a pixel, round up, otherwise floor it
35651         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35652         cols = Math[ mathMethod ]( cols );
35653         this.cols = Math.max( cols, 1 );
35654         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35655         
35656          // padding positioning..
35657         var totalColWidth = this.cols * this.columnWidth;
35658         var padavail = this.containerWidth - totalColWidth;
35659         // so for 2 columns - we need 3 'pads'
35660         
35661         var padNeeded = (1+this.cols) * this.padWidth;
35662         
35663         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35664         
35665         this.columnWidth += padExtra
35666         //this.padWidth = Math.floor(padavail /  ( this.cols));
35667         
35668         // adjust colum width so that padding is fixed??
35669         
35670         // we have 3 columns ... total = width * 3
35671         // we have X left over... that should be used by 
35672         
35673         //if (this.expandC) {
35674             
35675         //}
35676         
35677         
35678         
35679     },
35680     
35681     getContainerWidth : function()
35682     {
35683        /* // container is parent if fit width
35684         var container = this.isFitWidth ? this.element.parentNode : this.element;
35685         // check that this.size and size are there
35686         // IE8 triggers resize on body size change, so they might not be
35687         
35688         var size = getSize( container );  //FIXME
35689         this.containerWidth = size && size.innerWidth; //FIXME
35690         */
35691          
35692         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35693         
35694     },
35695     
35696     _getItemLayoutPosition : function( item )  // what is item?
35697     {
35698         // we resize the item to our columnWidth..
35699       
35700         item.setWidth(this.columnWidth);
35701         item.autoBoxAdjust  = false;
35702         
35703         var sz = item.getSize();
35704  
35705         // how many columns does this brick span
35706         var remainder = this.containerWidth % this.columnWidth;
35707         
35708         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35709         // round if off by 1 pixel, otherwise use ceil
35710         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35711         colSpan = Math.min( colSpan, this.cols );
35712         
35713         // normally this should be '1' as we dont' currently allow multi width columns..
35714         
35715         var colGroup = this._getColGroup( colSpan );
35716         // get the minimum Y value from the columns
35717         var minimumY = Math.min.apply( Math, colGroup );
35718         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35719         
35720         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35721          
35722         // position the brick
35723         var position = {
35724             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35725             y: this.currentSize.y + minimumY + this.padHeight
35726         };
35727         
35728         Roo.log(position);
35729         // apply setHeight to necessary columns
35730         var setHeight = minimumY + sz.height + this.padHeight;
35731         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35732         
35733         var setSpan = this.cols + 1 - colGroup.length;
35734         for ( var i = 0; i < setSpan; i++ ) {
35735           this.colYs[ shortColIndex + i ] = setHeight ;
35736         }
35737       
35738         return position;
35739     },
35740     
35741     /**
35742      * @param {Number} colSpan - number of columns the element spans
35743      * @returns {Array} colGroup
35744      */
35745     _getColGroup : function( colSpan )
35746     {
35747         if ( colSpan < 2 ) {
35748           // if brick spans only one column, use all the column Ys
35749           return this.colYs;
35750         }
35751       
35752         var colGroup = [];
35753         // how many different places could this brick fit horizontally
35754         var groupCount = this.cols + 1 - colSpan;
35755         // for each group potential horizontal position
35756         for ( var i = 0; i < groupCount; i++ ) {
35757           // make an array of colY values for that one group
35758           var groupColYs = this.colYs.slice( i, i + colSpan );
35759           // and get the max value of the array
35760           colGroup[i] = Math.max.apply( Math, groupColYs );
35761         }
35762         return colGroup;
35763     },
35764     /*
35765     _manageStamp : function( stamp )
35766     {
35767         var stampSize =  stamp.getSize();
35768         var offset = stamp.getBox();
35769         // get the columns that this stamp affects
35770         var firstX = this.isOriginLeft ? offset.x : offset.right;
35771         var lastX = firstX + stampSize.width;
35772         var firstCol = Math.floor( firstX / this.columnWidth );
35773         firstCol = Math.max( 0, firstCol );
35774         
35775         var lastCol = Math.floor( lastX / this.columnWidth );
35776         // lastCol should not go over if multiple of columnWidth #425
35777         lastCol -= lastX % this.columnWidth ? 0 : 1;
35778         lastCol = Math.min( this.cols - 1, lastCol );
35779         
35780         // set colYs to bottom of the stamp
35781         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35782             stampSize.height;
35783             
35784         for ( var i = firstCol; i <= lastCol; i++ ) {
35785           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35786         }
35787     },
35788     */
35789     
35790     _getContainerSize : function()
35791     {
35792         this.maxY = Math.max.apply( Math, this.colYs );
35793         var size = {
35794             height: this.maxY
35795         };
35796       
35797         if ( this.isFitWidth ) {
35798             size.width = this._getContainerFitWidth();
35799         }
35800       
35801         return size;
35802     },
35803     
35804     _getContainerFitWidth : function()
35805     {
35806         var unusedCols = 0;
35807         // count unused columns
35808         var i = this.cols;
35809         while ( --i ) {
35810           if ( this.colYs[i] !== 0 ) {
35811             break;
35812           }
35813           unusedCols++;
35814         }
35815         // fit container to columns that have been used
35816         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35817     },
35818     
35819     needsResizeLayout : function()
35820     {
35821         var previousWidth = this.containerWidth;
35822         this.getContainerWidth();
35823         return previousWidth !== this.containerWidth;
35824     }
35825  
35826 });
35827
35828  
35829
35830  /*
35831  * - LGPL
35832  *
35833  * element
35834  * 
35835  */
35836
35837 /**
35838  * @class Roo.bootstrap.MasonryBrick
35839  * @extends Roo.bootstrap.Component
35840  * Bootstrap MasonryBrick class
35841  * 
35842  * @constructor
35843  * Create a new MasonryBrick
35844  * @param {Object} config The config object
35845  */
35846
35847 Roo.bootstrap.MasonryBrick = function(config){
35848     
35849     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35850     
35851     Roo.bootstrap.MasonryBrick.register(this);
35852     
35853     this.addEvents({
35854         // raw events
35855         /**
35856          * @event click
35857          * When a MasonryBrick is clcik
35858          * @param {Roo.bootstrap.MasonryBrick} this
35859          * @param {Roo.EventObject} e
35860          */
35861         "click" : true
35862     });
35863 };
35864
35865 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35866     
35867     /**
35868      * @cfg {String} title
35869      */   
35870     title : '',
35871     /**
35872      * @cfg {String} html
35873      */   
35874     html : '',
35875     /**
35876      * @cfg {String} bgimage
35877      */   
35878     bgimage : '',
35879     /**
35880      * @cfg {String} videourl
35881      */   
35882     videourl : '',
35883     /**
35884      * @cfg {String} cls
35885      */   
35886     cls : '',
35887     /**
35888      * @cfg {String} href
35889      */   
35890     href : '',
35891     /**
35892      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35893      */   
35894     size : 'xs',
35895     
35896     /**
35897      * @cfg {String} placetitle (center|bottom)
35898      */   
35899     placetitle : '',
35900     
35901     /**
35902      * @cfg {Boolean} isFitContainer defalut true
35903      */   
35904     isFitContainer : true, 
35905     
35906     /**
35907      * @cfg {Boolean} preventDefault defalut false
35908      */   
35909     preventDefault : false, 
35910     
35911     /**
35912      * @cfg {Boolean} inverse defalut false
35913      */   
35914     maskInverse : false, 
35915     
35916     getAutoCreate : function()
35917     {
35918         if(!this.isFitContainer){
35919             return this.getSplitAutoCreate();
35920         }
35921         
35922         var cls = 'masonry-brick masonry-brick-full';
35923         
35924         if(this.href.length){
35925             cls += ' masonry-brick-link';
35926         }
35927         
35928         if(this.bgimage.length){
35929             cls += ' masonry-brick-image';
35930         }
35931         
35932         if(this.maskInverse){
35933             cls += ' mask-inverse';
35934         }
35935         
35936         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35937             cls += ' enable-mask';
35938         }
35939         
35940         if(this.size){
35941             cls += ' masonry-' + this.size + '-brick';
35942         }
35943         
35944         if(this.placetitle.length){
35945             
35946             switch (this.placetitle) {
35947                 case 'center' :
35948                     cls += ' masonry-center-title';
35949                     break;
35950                 case 'bottom' :
35951                     cls += ' masonry-bottom-title';
35952                     break;
35953                 default:
35954                     break;
35955             }
35956             
35957         } else {
35958             if(!this.html.length && !this.bgimage.length){
35959                 cls += ' masonry-center-title';
35960             }
35961
35962             if(!this.html.length && this.bgimage.length){
35963                 cls += ' masonry-bottom-title';
35964             }
35965         }
35966         
35967         if(this.cls){
35968             cls += ' ' + this.cls;
35969         }
35970         
35971         var cfg = {
35972             tag: (this.href.length) ? 'a' : 'div',
35973             cls: cls,
35974             cn: [
35975                 {
35976                     tag: 'div',
35977                     cls: 'masonry-brick-mask'
35978                 },
35979                 {
35980                     tag: 'div',
35981                     cls: 'masonry-brick-paragraph',
35982                     cn: []
35983                 }
35984             ]
35985         };
35986         
35987         if(this.href.length){
35988             cfg.href = this.href;
35989         }
35990         
35991         var cn = cfg.cn[1].cn;
35992         
35993         if(this.title.length){
35994             cn.push({
35995                 tag: 'h4',
35996                 cls: 'masonry-brick-title',
35997                 html: this.title
35998             });
35999         }
36000         
36001         if(this.html.length){
36002             cn.push({
36003                 tag: 'p',
36004                 cls: 'masonry-brick-text',
36005                 html: this.html
36006             });
36007         }
36008         
36009         if (!this.title.length && !this.html.length) {
36010             cfg.cn[1].cls += ' hide';
36011         }
36012         
36013         if(this.bgimage.length){
36014             cfg.cn.push({
36015                 tag: 'img',
36016                 cls: 'masonry-brick-image-view',
36017                 src: this.bgimage
36018             });
36019         }
36020         
36021         if(this.videourl.length){
36022             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36023             // youtube support only?
36024             cfg.cn.push({
36025                 tag: 'iframe',
36026                 cls: 'masonry-brick-image-view',
36027                 src: vurl,
36028                 frameborder : 0,
36029                 allowfullscreen : true
36030             });
36031         }
36032         
36033         return cfg;
36034         
36035     },
36036     
36037     getSplitAutoCreate : function()
36038     {
36039         var cls = 'masonry-brick masonry-brick-split';
36040         
36041         if(this.href.length){
36042             cls += ' masonry-brick-link';
36043         }
36044         
36045         if(this.bgimage.length){
36046             cls += ' masonry-brick-image';
36047         }
36048         
36049         if(this.size){
36050             cls += ' masonry-' + this.size + '-brick';
36051         }
36052         
36053         switch (this.placetitle) {
36054             case 'center' :
36055                 cls += ' masonry-center-title';
36056                 break;
36057             case 'bottom' :
36058                 cls += ' masonry-bottom-title';
36059                 break;
36060             default:
36061                 if(!this.bgimage.length){
36062                     cls += ' masonry-center-title';
36063                 }
36064
36065                 if(this.bgimage.length){
36066                     cls += ' masonry-bottom-title';
36067                 }
36068                 break;
36069         }
36070         
36071         if(this.cls){
36072             cls += ' ' + this.cls;
36073         }
36074         
36075         var cfg = {
36076             tag: (this.href.length) ? 'a' : 'div',
36077             cls: cls,
36078             cn: [
36079                 {
36080                     tag: 'div',
36081                     cls: 'masonry-brick-split-head',
36082                     cn: [
36083                         {
36084                             tag: 'div',
36085                             cls: 'masonry-brick-paragraph',
36086                             cn: []
36087                         }
36088                     ]
36089                 },
36090                 {
36091                     tag: 'div',
36092                     cls: 'masonry-brick-split-body',
36093                     cn: []
36094                 }
36095             ]
36096         };
36097         
36098         if(this.href.length){
36099             cfg.href = this.href;
36100         }
36101         
36102         if(this.title.length){
36103             cfg.cn[0].cn[0].cn.push({
36104                 tag: 'h4',
36105                 cls: 'masonry-brick-title',
36106                 html: this.title
36107             });
36108         }
36109         
36110         if(this.html.length){
36111             cfg.cn[1].cn.push({
36112                 tag: 'p',
36113                 cls: 'masonry-brick-text',
36114                 html: this.html
36115             });
36116         }
36117
36118         if(this.bgimage.length){
36119             cfg.cn[0].cn.push({
36120                 tag: 'img',
36121                 cls: 'masonry-brick-image-view',
36122                 src: this.bgimage
36123             });
36124         }
36125         
36126         if(this.videourl.length){
36127             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36128             // youtube support only?
36129             cfg.cn[0].cn.cn.push({
36130                 tag: 'iframe',
36131                 cls: 'masonry-brick-image-view',
36132                 src: vurl,
36133                 frameborder : 0,
36134                 allowfullscreen : true
36135             });
36136         }
36137         
36138         return cfg;
36139     },
36140     
36141     initEvents: function() 
36142     {
36143         switch (this.size) {
36144             case 'xs' :
36145                 this.x = 1;
36146                 this.y = 1;
36147                 break;
36148             case 'sm' :
36149                 this.x = 2;
36150                 this.y = 2;
36151                 break;
36152             case 'md' :
36153             case 'md-left' :
36154             case 'md-right' :
36155                 this.x = 3;
36156                 this.y = 3;
36157                 break;
36158             case 'tall' :
36159                 this.x = 2;
36160                 this.y = 3;
36161                 break;
36162             case 'wide' :
36163                 this.x = 3;
36164                 this.y = 2;
36165                 break;
36166             case 'wide-thin' :
36167                 this.x = 3;
36168                 this.y = 1;
36169                 break;
36170                         
36171             default :
36172                 break;
36173         }
36174         
36175         if(Roo.isTouch){
36176             this.el.on('touchstart', this.onTouchStart, this);
36177             this.el.on('touchmove', this.onTouchMove, this);
36178             this.el.on('touchend', this.onTouchEnd, this);
36179             this.el.on('contextmenu', this.onContextMenu, this);
36180         } else {
36181             this.el.on('mouseenter'  ,this.enter, this);
36182             this.el.on('mouseleave', this.leave, this);
36183             this.el.on('click', this.onClick, this);
36184         }
36185         
36186         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36187             this.parent().bricks.push(this);   
36188         }
36189         
36190     },
36191     
36192     onClick: function(e, el)
36193     {
36194         var time = this.endTimer - this.startTimer;
36195         // Roo.log(e.preventDefault());
36196         if(Roo.isTouch){
36197             if(time > 1000){
36198                 e.preventDefault();
36199                 return;
36200             }
36201         }
36202         
36203         if(!this.preventDefault){
36204             return;
36205         }
36206         
36207         e.preventDefault();
36208         
36209         if (this.activeClass != '') {
36210             this.selectBrick();
36211         }
36212         
36213         this.fireEvent('click', this, e);
36214     },
36215     
36216     enter: function(e, el)
36217     {
36218         e.preventDefault();
36219         
36220         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36221             return;
36222         }
36223         
36224         if(this.bgimage.length && this.html.length){
36225             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36226         }
36227     },
36228     
36229     leave: function(e, el)
36230     {
36231         e.preventDefault();
36232         
36233         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36234             return;
36235         }
36236         
36237         if(this.bgimage.length && this.html.length){
36238             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36239         }
36240     },
36241     
36242     onTouchStart: function(e, el)
36243     {
36244 //        e.preventDefault();
36245         
36246         this.touchmoved = false;
36247         
36248         if(!this.isFitContainer){
36249             return;
36250         }
36251         
36252         if(!this.bgimage.length || !this.html.length){
36253             return;
36254         }
36255         
36256         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36257         
36258         this.timer = new Date().getTime();
36259         
36260     },
36261     
36262     onTouchMove: function(e, el)
36263     {
36264         this.touchmoved = true;
36265     },
36266     
36267     onContextMenu : function(e,el)
36268     {
36269         e.preventDefault();
36270         e.stopPropagation();
36271         return false;
36272     },
36273     
36274     onTouchEnd: function(e, el)
36275     {
36276 //        e.preventDefault();
36277         
36278         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36279         
36280             this.leave(e,el);
36281             
36282             return;
36283         }
36284         
36285         if(!this.bgimage.length || !this.html.length){
36286             
36287             if(this.href.length){
36288                 window.location.href = this.href;
36289             }
36290             
36291             return;
36292         }
36293         
36294         if(!this.isFitContainer){
36295             return;
36296         }
36297         
36298         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36299         
36300         window.location.href = this.href;
36301     },
36302     
36303     //selection on single brick only
36304     selectBrick : function() {
36305         
36306         if (!this.parentId) {
36307             return;
36308         }
36309         
36310         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36311         var index = m.selectedBrick.indexOf(this.id);
36312         
36313         if ( index > -1) {
36314             m.selectedBrick.splice(index,1);
36315             this.el.removeClass(this.activeClass);
36316             return;
36317         }
36318         
36319         for(var i = 0; i < m.selectedBrick.length; i++) {
36320             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36321             b.el.removeClass(b.activeClass);
36322         }
36323         
36324         m.selectedBrick = [];
36325         
36326         m.selectedBrick.push(this.id);
36327         this.el.addClass(this.activeClass);
36328         return;
36329     },
36330     
36331     isSelected : function(){
36332         return this.el.hasClass(this.activeClass);
36333         
36334     }
36335 });
36336
36337 Roo.apply(Roo.bootstrap.MasonryBrick, {
36338     
36339     //groups: {},
36340     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36341      /**
36342     * register a Masonry Brick
36343     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36344     */
36345     
36346     register : function(brick)
36347     {
36348         //this.groups[brick.id] = brick;
36349         this.groups.add(brick.id, brick);
36350     },
36351     /**
36352     * fetch a  masonry brick based on the masonry brick ID
36353     * @param {string} the masonry brick to add
36354     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36355     */
36356     
36357     get: function(brick_id) 
36358     {
36359         // if (typeof(this.groups[brick_id]) == 'undefined') {
36360         //     return false;
36361         // }
36362         // return this.groups[brick_id] ;
36363         
36364         if(this.groups.key(brick_id)) {
36365             return this.groups.key(brick_id);
36366         }
36367         
36368         return false;
36369     }
36370     
36371     
36372     
36373 });
36374
36375  /*
36376  * - LGPL
36377  *
36378  * element
36379  * 
36380  */
36381
36382 /**
36383  * @class Roo.bootstrap.Brick
36384  * @extends Roo.bootstrap.Component
36385  * Bootstrap Brick class
36386  * 
36387  * @constructor
36388  * Create a new Brick
36389  * @param {Object} config The config object
36390  */
36391
36392 Roo.bootstrap.Brick = function(config){
36393     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36394     
36395     this.addEvents({
36396         // raw events
36397         /**
36398          * @event click
36399          * When a Brick is click
36400          * @param {Roo.bootstrap.Brick} this
36401          * @param {Roo.EventObject} e
36402          */
36403         "click" : true
36404     });
36405 };
36406
36407 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36408     
36409     /**
36410      * @cfg {String} title
36411      */   
36412     title : '',
36413     /**
36414      * @cfg {String} html
36415      */   
36416     html : '',
36417     /**
36418      * @cfg {String} bgimage
36419      */   
36420     bgimage : '',
36421     /**
36422      * @cfg {String} cls
36423      */   
36424     cls : '',
36425     /**
36426      * @cfg {String} href
36427      */   
36428     href : '',
36429     /**
36430      * @cfg {String} video
36431      */   
36432     video : '',
36433     /**
36434      * @cfg {Boolean} square
36435      */   
36436     square : true,
36437     
36438     getAutoCreate : function()
36439     {
36440         var cls = 'roo-brick';
36441         
36442         if(this.href.length){
36443             cls += ' roo-brick-link';
36444         }
36445         
36446         if(this.bgimage.length){
36447             cls += ' roo-brick-image';
36448         }
36449         
36450         if(!this.html.length && !this.bgimage.length){
36451             cls += ' roo-brick-center-title';
36452         }
36453         
36454         if(!this.html.length && this.bgimage.length){
36455             cls += ' roo-brick-bottom-title';
36456         }
36457         
36458         if(this.cls){
36459             cls += ' ' + this.cls;
36460         }
36461         
36462         var cfg = {
36463             tag: (this.href.length) ? 'a' : 'div',
36464             cls: cls,
36465             cn: [
36466                 {
36467                     tag: 'div',
36468                     cls: 'roo-brick-paragraph',
36469                     cn: []
36470                 }
36471             ]
36472         };
36473         
36474         if(this.href.length){
36475             cfg.href = this.href;
36476         }
36477         
36478         var cn = cfg.cn[0].cn;
36479         
36480         if(this.title.length){
36481             cn.push({
36482                 tag: 'h4',
36483                 cls: 'roo-brick-title',
36484                 html: this.title
36485             });
36486         }
36487         
36488         if(this.html.length){
36489             cn.push({
36490                 tag: 'p',
36491                 cls: 'roo-brick-text',
36492                 html: this.html
36493             });
36494         } else {
36495             cn.cls += ' hide';
36496         }
36497         
36498         if(this.bgimage.length){
36499             cfg.cn.push({
36500                 tag: 'img',
36501                 cls: 'roo-brick-image-view',
36502                 src: this.bgimage
36503             });
36504         }
36505         
36506         return cfg;
36507     },
36508     
36509     initEvents: function() 
36510     {
36511         if(this.title.length || this.html.length){
36512             this.el.on('mouseenter'  ,this.enter, this);
36513             this.el.on('mouseleave', this.leave, this);
36514         }
36515         
36516         Roo.EventManager.onWindowResize(this.resize, this); 
36517         
36518         if(this.bgimage.length){
36519             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36520             this.imageEl.on('load', this.onImageLoad, this);
36521             return;
36522         }
36523         
36524         this.resize();
36525     },
36526     
36527     onImageLoad : function()
36528     {
36529         this.resize();
36530     },
36531     
36532     resize : function()
36533     {
36534         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36535         
36536         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36537         
36538         if(this.bgimage.length){
36539             var image = this.el.select('.roo-brick-image-view', true).first();
36540             
36541             image.setWidth(paragraph.getWidth());
36542             
36543             if(this.square){
36544                 image.setHeight(paragraph.getWidth());
36545             }
36546             
36547             this.el.setHeight(image.getHeight());
36548             paragraph.setHeight(image.getHeight());
36549             
36550         }
36551         
36552     },
36553     
36554     enter: function(e, el)
36555     {
36556         e.preventDefault();
36557         
36558         if(this.bgimage.length){
36559             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36560             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36561         }
36562     },
36563     
36564     leave: function(e, el)
36565     {
36566         e.preventDefault();
36567         
36568         if(this.bgimage.length){
36569             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36570             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36571         }
36572     }
36573     
36574 });
36575
36576  
36577
36578  /*
36579  * - LGPL
36580  *
36581  * Number field 
36582  */
36583
36584 /**
36585  * @class Roo.bootstrap.form.NumberField
36586  * @extends Roo.bootstrap.form.Input
36587  * Bootstrap NumberField class
36588  * 
36589  * 
36590  * 
36591  * 
36592  * @constructor
36593  * Create a new NumberField
36594  * @param {Object} config The config object
36595  */
36596
36597 Roo.bootstrap.form.NumberField = function(config){
36598     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36599 };
36600
36601 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36602     
36603     /**
36604      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36605      */
36606     allowDecimals : true,
36607     /**
36608      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36609      */
36610     decimalSeparator : ".",
36611     /**
36612      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36613      */
36614     decimalPrecision : 2,
36615     /**
36616      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36617      */
36618     allowNegative : true,
36619     
36620     /**
36621      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36622      */
36623     allowZero: true,
36624     /**
36625      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36626      */
36627     minValue : Number.NEGATIVE_INFINITY,
36628     /**
36629      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36630      */
36631     maxValue : Number.MAX_VALUE,
36632     /**
36633      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36634      */
36635     minText : "The minimum value for this field is {0}",
36636     /**
36637      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36638      */
36639     maxText : "The maximum value for this field is {0}",
36640     /**
36641      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36642      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36643      */
36644     nanText : "{0} is not a valid number",
36645     /**
36646      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36647      */
36648     thousandsDelimiter : false,
36649     /**
36650      * @cfg {String} valueAlign alignment of value
36651      */
36652     valueAlign : "left",
36653
36654     getAutoCreate : function()
36655     {
36656         var hiddenInput = {
36657             tag: 'input',
36658             type: 'hidden',
36659             id: Roo.id(),
36660             cls: 'hidden-number-input'
36661         };
36662         
36663         if (this.name) {
36664             hiddenInput.name = this.name;
36665         }
36666         
36667         this.name = '';
36668         
36669         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36670         
36671         this.name = hiddenInput.name;
36672         
36673         if(cfg.cn.length > 0) {
36674             cfg.cn.push(hiddenInput);
36675         }
36676         
36677         return cfg;
36678     },
36679
36680     // private
36681     initEvents : function()
36682     {   
36683         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36684         
36685         var allowed = "0123456789";
36686         
36687         if(this.allowDecimals){
36688             allowed += this.decimalSeparator;
36689         }
36690         
36691         if(this.allowNegative){
36692             allowed += "-";
36693         }
36694         
36695         if(this.thousandsDelimiter) {
36696             allowed += ",";
36697         }
36698         
36699         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36700         
36701         var keyPress = function(e){
36702             
36703             var k = e.getKey();
36704             
36705             var c = e.getCharCode();
36706             
36707             if(
36708                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36709                     allowed.indexOf(String.fromCharCode(c)) === -1
36710             ){
36711                 e.stopEvent();
36712                 return;
36713             }
36714             
36715             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36716                 return;
36717             }
36718             
36719             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36720                 e.stopEvent();
36721             }
36722         };
36723         
36724         this.el.on("keypress", keyPress, this);
36725     },
36726     
36727     validateValue : function(value)
36728     {
36729         
36730         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36731             return false;
36732         }
36733         
36734         var num = this.parseValue(value);
36735         
36736         if(isNaN(num)){
36737             this.markInvalid(String.format(this.nanText, value));
36738             return false;
36739         }
36740         
36741         if(num < this.minValue){
36742             this.markInvalid(String.format(this.minText, this.minValue));
36743             return false;
36744         }
36745         
36746         if(num > this.maxValue){
36747             this.markInvalid(String.format(this.maxText, this.maxValue));
36748             return false;
36749         }
36750         
36751         return true;
36752     },
36753
36754     getValue : function()
36755     {
36756         var v = this.hiddenEl().getValue();
36757         
36758         return this.fixPrecision(this.parseValue(v));
36759     },
36760
36761     parseValue : function(value)
36762     {
36763         if(this.thousandsDelimiter) {
36764             value += "";
36765             r = new RegExp(",", "g");
36766             value = value.replace(r, "");
36767         }
36768         
36769         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36770         return isNaN(value) ? '' : value;
36771     },
36772
36773     fixPrecision : function(value)
36774     {
36775         if(this.thousandsDelimiter) {
36776             value += "";
36777             r = new RegExp(",", "g");
36778             value = value.replace(r, "");
36779         }
36780         
36781         var nan = isNaN(value);
36782         
36783         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36784             return nan ? '' : value;
36785         }
36786         return parseFloat(value).toFixed(this.decimalPrecision);
36787     },
36788
36789     setValue : function(v)
36790     {
36791         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36792         
36793         this.value = v;
36794         
36795         if(this.rendered){
36796             
36797             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36798             
36799             this.inputEl().dom.value = (v == '') ? '' :
36800                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36801             
36802             if(!this.allowZero && v === '0') {
36803                 this.hiddenEl().dom.value = '';
36804                 this.inputEl().dom.value = '';
36805             }
36806             
36807             this.validate();
36808         }
36809     },
36810
36811     decimalPrecisionFcn : function(v)
36812     {
36813         return Math.floor(v);
36814     },
36815
36816     beforeBlur : function()
36817     {
36818         var v = this.parseValue(this.getRawValue());
36819         
36820         if(v || v === 0 || v === ''){
36821             this.setValue(v);
36822         }
36823     },
36824     
36825     hiddenEl : function()
36826     {
36827         return this.el.select('input.hidden-number-input',true).first();
36828     }
36829     
36830 });
36831
36832  
36833
36834 /*
36835 * Licence: LGPL
36836 */
36837
36838 /**
36839  * @class Roo.bootstrap.DocumentSlider
36840  * @extends Roo.bootstrap.Component
36841  * Bootstrap DocumentSlider class
36842  * 
36843  * @constructor
36844  * Create a new DocumentViewer
36845  * @param {Object} config The config object
36846  */
36847
36848 Roo.bootstrap.DocumentSlider = function(config){
36849     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36850     
36851     this.files = [];
36852     
36853     this.addEvents({
36854         /**
36855          * @event initial
36856          * Fire after initEvent
36857          * @param {Roo.bootstrap.DocumentSlider} this
36858          */
36859         "initial" : true,
36860         /**
36861          * @event update
36862          * Fire after update
36863          * @param {Roo.bootstrap.DocumentSlider} this
36864          */
36865         "update" : true,
36866         /**
36867          * @event click
36868          * Fire after click
36869          * @param {Roo.bootstrap.DocumentSlider} this
36870          */
36871         "click" : true
36872     });
36873 };
36874
36875 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36876     
36877     files : false,
36878     
36879     indicator : 0,
36880     
36881     getAutoCreate : function()
36882     {
36883         var cfg = {
36884             tag : 'div',
36885             cls : 'roo-document-slider',
36886             cn : [
36887                 {
36888                     tag : 'div',
36889                     cls : 'roo-document-slider-header',
36890                     cn : [
36891                         {
36892                             tag : 'div',
36893                             cls : 'roo-document-slider-header-title'
36894                         }
36895                     ]
36896                 },
36897                 {
36898                     tag : 'div',
36899                     cls : 'roo-document-slider-body',
36900                     cn : [
36901                         {
36902                             tag : 'div',
36903                             cls : 'roo-document-slider-prev',
36904                             cn : [
36905                                 {
36906                                     tag : 'i',
36907                                     cls : 'fa fa-chevron-left'
36908                                 }
36909                             ]
36910                         },
36911                         {
36912                             tag : 'div',
36913                             cls : 'roo-document-slider-thumb',
36914                             cn : [
36915                                 {
36916                                     tag : 'img',
36917                                     cls : 'roo-document-slider-image'
36918                                 }
36919                             ]
36920                         },
36921                         {
36922                             tag : 'div',
36923                             cls : 'roo-document-slider-next',
36924                             cn : [
36925                                 {
36926                                     tag : 'i',
36927                                     cls : 'fa fa-chevron-right'
36928                                 }
36929                             ]
36930                         }
36931                     ]
36932                 }
36933             ]
36934         };
36935         
36936         return cfg;
36937     },
36938     
36939     initEvents : function()
36940     {
36941         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36942         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36943         
36944         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36945         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36946         
36947         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36948         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36949         
36950         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36951         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36952         
36953         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36954         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36955         
36956         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36957         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36958         
36959         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36960         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36961         
36962         this.thumbEl.on('click', this.onClick, this);
36963         
36964         this.prevIndicator.on('click', this.prev, this);
36965         
36966         this.nextIndicator.on('click', this.next, this);
36967         
36968     },
36969     
36970     initial : function()
36971     {
36972         if(this.files.length){
36973             this.indicator = 1;
36974             this.update()
36975         }
36976         
36977         this.fireEvent('initial', this);
36978     },
36979     
36980     update : function()
36981     {
36982         this.imageEl.attr('src', this.files[this.indicator - 1]);
36983         
36984         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36985         
36986         this.prevIndicator.show();
36987         
36988         if(this.indicator == 1){
36989             this.prevIndicator.hide();
36990         }
36991         
36992         this.nextIndicator.show();
36993         
36994         if(this.indicator == this.files.length){
36995             this.nextIndicator.hide();
36996         }
36997         
36998         this.thumbEl.scrollTo('top');
36999         
37000         this.fireEvent('update', this);
37001     },
37002     
37003     onClick : function(e)
37004     {
37005         e.preventDefault();
37006         
37007         this.fireEvent('click', this);
37008     },
37009     
37010     prev : function(e)
37011     {
37012         e.preventDefault();
37013         
37014         this.indicator = Math.max(1, this.indicator - 1);
37015         
37016         this.update();
37017     },
37018     
37019     next : function(e)
37020     {
37021         e.preventDefault();
37022         
37023         this.indicator = Math.min(this.files.length, this.indicator + 1);
37024         
37025         this.update();
37026     }
37027 });
37028 /*
37029  * - LGPL
37030  *
37031  * RadioSet
37032  *
37033  *
37034  */
37035
37036 /**
37037  * @class Roo.bootstrap.form.RadioSet
37038  * @extends Roo.bootstrap.form.Input
37039  * @children Roo.bootstrap.form.Radio
37040  * Bootstrap RadioSet class
37041  * @cfg {String} indicatorpos (left|right) default left
37042  * @cfg {Boolean} inline (true|false) inline the element (default true)
37043  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37044  * @constructor
37045  * Create a new RadioSet
37046  * @param {Object} config The config object
37047  */
37048
37049 Roo.bootstrap.form.RadioSet = function(config){
37050     
37051     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37052     
37053     this.radioes = [];
37054     
37055     Roo.bootstrap.form.RadioSet.register(this);
37056     
37057     this.addEvents({
37058         /**
37059         * @event check
37060         * Fires when the element is checked or unchecked.
37061         * @param {Roo.bootstrap.form.RadioSet} this This radio
37062         * @param {Roo.bootstrap.form.Radio} item The checked item
37063         */
37064        check : true,
37065        /**
37066         * @event click
37067         * Fires when the element is click.
37068         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37069         * @param {Roo.bootstrap.form.Radio} item The checked item
37070         * @param {Roo.EventObject} e The event object
37071         */
37072        click : true
37073     });
37074     
37075 };
37076
37077 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37078
37079     radioes : false,
37080     
37081     inline : true,
37082     
37083     weight : '',
37084     
37085     indicatorpos : 'left',
37086     
37087     getAutoCreate : function()
37088     {
37089         var label = {
37090             tag : 'label',
37091             cls : 'roo-radio-set-label',
37092             cn : [
37093                 {
37094                     tag : 'span',
37095                     html : this.fieldLabel
37096                 }
37097             ]
37098         };
37099         if (Roo.bootstrap.version == 3) {
37100             
37101             
37102             if(this.indicatorpos == 'left'){
37103                 label.cn.unshift({
37104                     tag : 'i',
37105                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37106                     tooltip : 'This field is required'
37107                 });
37108             } else {
37109                 label.cn.push({
37110                     tag : 'i',
37111                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37112                     tooltip : 'This field is required'
37113                 });
37114             }
37115         }
37116         var items = {
37117             tag : 'div',
37118             cls : 'roo-radio-set-items'
37119         };
37120         
37121         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37122         
37123         if (align === 'left' && this.fieldLabel.length) {
37124             
37125             items = {
37126                 cls : "roo-radio-set-right", 
37127                 cn: [
37128                     items
37129                 ]
37130             };
37131             
37132             if(this.labelWidth > 12){
37133                 label.style = "width: " + this.labelWidth + 'px';
37134             }
37135             
37136             if(this.labelWidth < 13 && this.labelmd == 0){
37137                 this.labelmd = this.labelWidth;
37138             }
37139             
37140             if(this.labellg > 0){
37141                 label.cls += ' col-lg-' + this.labellg;
37142                 items.cls += ' col-lg-' + (12 - this.labellg);
37143             }
37144             
37145             if(this.labelmd > 0){
37146                 label.cls += ' col-md-' + this.labelmd;
37147                 items.cls += ' col-md-' + (12 - this.labelmd);
37148             }
37149             
37150             if(this.labelsm > 0){
37151                 label.cls += ' col-sm-' + this.labelsm;
37152                 items.cls += ' col-sm-' + (12 - this.labelsm);
37153             }
37154             
37155             if(this.labelxs > 0){
37156                 label.cls += ' col-xs-' + this.labelxs;
37157                 items.cls += ' col-xs-' + (12 - this.labelxs);
37158             }
37159         }
37160         
37161         var cfg = {
37162             tag : 'div',
37163             cls : 'roo-radio-set',
37164             cn : [
37165                 {
37166                     tag : 'input',
37167                     cls : 'roo-radio-set-input',
37168                     type : 'hidden',
37169                     name : this.name,
37170                     value : this.value ? this.value :  ''
37171                 },
37172                 label,
37173                 items
37174             ]
37175         };
37176         
37177         if(this.weight.length){
37178             cfg.cls += ' roo-radio-' + this.weight;
37179         }
37180         
37181         if(this.inline) {
37182             cfg.cls += ' roo-radio-set-inline';
37183         }
37184         
37185         var settings=this;
37186         ['xs','sm','md','lg'].map(function(size){
37187             if (settings[size]) {
37188                 cfg.cls += ' col-' + size + '-' + settings[size];
37189             }
37190         });
37191         
37192         return cfg;
37193         
37194     },
37195
37196     initEvents : function()
37197     {
37198         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37199         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37200         
37201         if(!this.fieldLabel.length){
37202             this.labelEl.hide();
37203         }
37204         
37205         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37206         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37207         
37208         this.indicator = this.indicatorEl();
37209         
37210         if(this.indicator){
37211             this.indicator.addClass('invisible');
37212         }
37213         
37214         this.originalValue = this.getValue();
37215         
37216     },
37217     
37218     inputEl: function ()
37219     {
37220         return this.el.select('.roo-radio-set-input', true).first();
37221     },
37222     
37223     getChildContainer : function()
37224     {
37225         return this.itemsEl;
37226     },
37227     
37228     register : function(item)
37229     {
37230         this.radioes.push(item);
37231         
37232     },
37233     
37234     validate : function()
37235     {   
37236         if(this.getVisibilityEl().hasClass('hidden')){
37237             return true;
37238         }
37239         
37240         var valid = false;
37241         
37242         Roo.each(this.radioes, function(i){
37243             if(!i.checked){
37244                 return;
37245             }
37246             
37247             valid = true;
37248             return false;
37249         });
37250         
37251         if(this.allowBlank) {
37252             return true;
37253         }
37254         
37255         if(this.disabled || valid){
37256             this.markValid();
37257             return true;
37258         }
37259         
37260         this.markInvalid();
37261         return false;
37262         
37263     },
37264     
37265     markValid : function()
37266     {
37267         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37268             this.indicatorEl().removeClass('visible');
37269             this.indicatorEl().addClass('invisible');
37270         }
37271         
37272         
37273         if (Roo.bootstrap.version == 3) {
37274             this.el.removeClass([this.invalidClass, this.validClass]);
37275             this.el.addClass(this.validClass);
37276         } else {
37277             this.el.removeClass(['is-invalid','is-valid']);
37278             this.el.addClass(['is-valid']);
37279         }
37280         this.fireEvent('valid', this);
37281     },
37282     
37283     markInvalid : function(msg)
37284     {
37285         if(this.allowBlank || this.disabled){
37286             return;
37287         }
37288         
37289         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37290             this.indicatorEl().removeClass('invisible');
37291             this.indicatorEl().addClass('visible');
37292         }
37293         if (Roo.bootstrap.version == 3) {
37294             this.el.removeClass([this.invalidClass, this.validClass]);
37295             this.el.addClass(this.invalidClass);
37296         } else {
37297             this.el.removeClass(['is-invalid','is-valid']);
37298             this.el.addClass(['is-invalid']);
37299         }
37300         
37301         this.fireEvent('invalid', this, msg);
37302         
37303     },
37304     
37305     setValue : function(v, suppressEvent)
37306     {   
37307         if(this.value === v){
37308             return;
37309         }
37310         
37311         this.value = v;
37312         
37313         if(this.rendered){
37314             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37315         }
37316         
37317         Roo.each(this.radioes, function(i){
37318             i.checked = false;
37319             i.el.removeClass('checked');
37320         });
37321         
37322         Roo.each(this.radioes, function(i){
37323             
37324             if(i.value === v || i.value.toString() === v.toString()){
37325                 i.checked = true;
37326                 i.el.addClass('checked');
37327                 
37328                 if(suppressEvent !== true){
37329                     this.fireEvent('check', this, i);
37330                 }
37331                 
37332                 return false;
37333             }
37334             
37335         }, this);
37336         
37337         this.validate();
37338     },
37339     
37340     clearInvalid : function(){
37341         
37342         if(!this.el || this.preventMark){
37343             return;
37344         }
37345         
37346         this.el.removeClass([this.invalidClass]);
37347         
37348         this.fireEvent('valid', this);
37349     }
37350     
37351 });
37352
37353 Roo.apply(Roo.bootstrap.form.RadioSet, {
37354     
37355     groups: {},
37356     
37357     register : function(set)
37358     {
37359         this.groups[set.name] = set;
37360     },
37361     
37362     get: function(name) 
37363     {
37364         if (typeof(this.groups[name]) == 'undefined') {
37365             return false;
37366         }
37367         
37368         return this.groups[name] ;
37369     }
37370     
37371 });
37372 /*
37373  * Based on:
37374  * Ext JS Library 1.1.1
37375  * Copyright(c) 2006-2007, Ext JS, LLC.
37376  *
37377  * Originally Released Under LGPL - original licence link has changed is not relivant.
37378  *
37379  * Fork - LGPL
37380  * <script type="text/javascript">
37381  */
37382
37383
37384 /**
37385  * @class Roo.bootstrap.SplitBar
37386  * @extends Roo.util.Observable
37387  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37388  * <br><br>
37389  * Usage:
37390  * <pre><code>
37391 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37392                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37393 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37394 split.minSize = 100;
37395 split.maxSize = 600;
37396 split.animate = true;
37397 split.on('moved', splitterMoved);
37398 </code></pre>
37399  * @constructor
37400  * Create a new SplitBar
37401  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37402  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37403  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37404  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37405                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37406                         position of the SplitBar).
37407  */
37408 Roo.bootstrap.SplitBar = function(cfg){
37409     
37410     /** @private */
37411     
37412     //{
37413     //  dragElement : elm
37414     //  resizingElement: el,
37415         // optional..
37416     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37417     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37418         // existingProxy ???
37419     //}
37420     
37421     this.el = Roo.get(cfg.dragElement, true);
37422     this.el.dom.unselectable = "on";
37423     /** @private */
37424     this.resizingEl = Roo.get(cfg.resizingElement, true);
37425
37426     /**
37427      * @private
37428      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37429      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37430      * @type Number
37431      */
37432     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37433     
37434     /**
37435      * The minimum size of the resizing element. (Defaults to 0)
37436      * @type Number
37437      */
37438     this.minSize = 0;
37439     
37440     /**
37441      * The maximum size of the resizing element. (Defaults to 2000)
37442      * @type Number
37443      */
37444     this.maxSize = 2000;
37445     
37446     /**
37447      * Whether to animate the transition to the new size
37448      * @type Boolean
37449      */
37450     this.animate = false;
37451     
37452     /**
37453      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37454      * @type Boolean
37455      */
37456     this.useShim = false;
37457     
37458     /** @private */
37459     this.shim = null;
37460     
37461     if(!cfg.existingProxy){
37462         /** @private */
37463         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37464     }else{
37465         this.proxy = Roo.get(cfg.existingProxy).dom;
37466     }
37467     /** @private */
37468     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37469     
37470     /** @private */
37471     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37472     
37473     /** @private */
37474     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37475     
37476     /** @private */
37477     this.dragSpecs = {};
37478     
37479     /**
37480      * @private The adapter to use to positon and resize elements
37481      */
37482     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37483     this.adapter.init(this);
37484     
37485     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37486         /** @private */
37487         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37488         this.el.addClass("roo-splitbar-h");
37489     }else{
37490         /** @private */
37491         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37492         this.el.addClass("roo-splitbar-v");
37493     }
37494     
37495     this.addEvents({
37496         /**
37497          * @event resize
37498          * Fires when the splitter is moved (alias for {@link #event-moved})
37499          * @param {Roo.bootstrap.SplitBar} this
37500          * @param {Number} newSize the new width or height
37501          */
37502         "resize" : true,
37503         /**
37504          * @event moved
37505          * Fires when the splitter is moved
37506          * @param {Roo.bootstrap.SplitBar} this
37507          * @param {Number} newSize the new width or height
37508          */
37509         "moved" : true,
37510         /**
37511          * @event beforeresize
37512          * Fires before the splitter is dragged
37513          * @param {Roo.bootstrap.SplitBar} this
37514          */
37515         "beforeresize" : true,
37516
37517         "beforeapply" : true
37518     });
37519
37520     Roo.util.Observable.call(this);
37521 };
37522
37523 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37524     onStartProxyDrag : function(x, y){
37525         this.fireEvent("beforeresize", this);
37526         if(!this.overlay){
37527             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37528             o.unselectable();
37529             o.enableDisplayMode("block");
37530             // all splitbars share the same overlay
37531             Roo.bootstrap.SplitBar.prototype.overlay = o;
37532         }
37533         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37534         this.overlay.show();
37535         Roo.get(this.proxy).setDisplayed("block");
37536         var size = this.adapter.getElementSize(this);
37537         this.activeMinSize = this.getMinimumSize();;
37538         this.activeMaxSize = this.getMaximumSize();;
37539         var c1 = size - this.activeMinSize;
37540         var c2 = Math.max(this.activeMaxSize - size, 0);
37541         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37542             this.dd.resetConstraints();
37543             this.dd.setXConstraint(
37544                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37545                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37546             );
37547             this.dd.setYConstraint(0, 0);
37548         }else{
37549             this.dd.resetConstraints();
37550             this.dd.setXConstraint(0, 0);
37551             this.dd.setYConstraint(
37552                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37553                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37554             );
37555          }
37556         this.dragSpecs.startSize = size;
37557         this.dragSpecs.startPoint = [x, y];
37558         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37559     },
37560     
37561     /** 
37562      * @private Called after the drag operation by the DDProxy
37563      */
37564     onEndProxyDrag : function(e){
37565         Roo.get(this.proxy).setDisplayed(false);
37566         var endPoint = Roo.lib.Event.getXY(e);
37567         if(this.overlay){
37568             this.overlay.hide();
37569         }
37570         var newSize;
37571         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37572             newSize = this.dragSpecs.startSize + 
37573                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37574                     endPoint[0] - this.dragSpecs.startPoint[0] :
37575                     this.dragSpecs.startPoint[0] - endPoint[0]
37576                 );
37577         }else{
37578             newSize = this.dragSpecs.startSize + 
37579                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37580                     endPoint[1] - this.dragSpecs.startPoint[1] :
37581                     this.dragSpecs.startPoint[1] - endPoint[1]
37582                 );
37583         }
37584         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37585         if(newSize != this.dragSpecs.startSize){
37586             if(this.fireEvent('beforeapply', this, newSize) !== false){
37587                 this.adapter.setElementSize(this, newSize);
37588                 this.fireEvent("moved", this, newSize);
37589                 this.fireEvent("resize", this, newSize);
37590             }
37591         }
37592     },
37593     
37594     /**
37595      * Get the adapter this SplitBar uses
37596      * @return The adapter object
37597      */
37598     getAdapter : function(){
37599         return this.adapter;
37600     },
37601     
37602     /**
37603      * Set the adapter this SplitBar uses
37604      * @param {Object} adapter A SplitBar adapter object
37605      */
37606     setAdapter : function(adapter){
37607         this.adapter = adapter;
37608         this.adapter.init(this);
37609     },
37610     
37611     /**
37612      * Gets the minimum size for the resizing element
37613      * @return {Number} The minimum size
37614      */
37615     getMinimumSize : function(){
37616         return this.minSize;
37617     },
37618     
37619     /**
37620      * Sets the minimum size for the resizing element
37621      * @param {Number} minSize The minimum size
37622      */
37623     setMinimumSize : function(minSize){
37624         this.minSize = minSize;
37625     },
37626     
37627     /**
37628      * Gets the maximum size for the resizing element
37629      * @return {Number} The maximum size
37630      */
37631     getMaximumSize : function(){
37632         return this.maxSize;
37633     },
37634     
37635     /**
37636      * Sets the maximum size for the resizing element
37637      * @param {Number} maxSize The maximum size
37638      */
37639     setMaximumSize : function(maxSize){
37640         this.maxSize = maxSize;
37641     },
37642     
37643     /**
37644      * Sets the initialize size for the resizing element
37645      * @param {Number} size The initial size
37646      */
37647     setCurrentSize : function(size){
37648         var oldAnimate = this.animate;
37649         this.animate = false;
37650         this.adapter.setElementSize(this, size);
37651         this.animate = oldAnimate;
37652     },
37653     
37654     /**
37655      * Destroy this splitbar. 
37656      * @param {Boolean} removeEl True to remove the element
37657      */
37658     destroy : function(removeEl){
37659         if(this.shim){
37660             this.shim.remove();
37661         }
37662         this.dd.unreg();
37663         this.proxy.parentNode.removeChild(this.proxy);
37664         if(removeEl){
37665             this.el.remove();
37666         }
37667     }
37668 });
37669
37670 /**
37671  * @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.
37672  */
37673 Roo.bootstrap.SplitBar.createProxy = function(dir){
37674     var proxy = new Roo.Element(document.createElement("div"));
37675     proxy.unselectable();
37676     var cls = 'roo-splitbar-proxy';
37677     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37678     document.body.appendChild(proxy.dom);
37679     return proxy.dom;
37680 };
37681
37682 /** 
37683  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37684  * Default Adapter. It assumes the splitter and resizing element are not positioned
37685  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37686  */
37687 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37688 };
37689
37690 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37691     // do nothing for now
37692     init : function(s){
37693     
37694     },
37695     /**
37696      * Called before drag operations to get the current size of the resizing element. 
37697      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37698      */
37699      getElementSize : function(s){
37700         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37701             return s.resizingEl.getWidth();
37702         }else{
37703             return s.resizingEl.getHeight();
37704         }
37705     },
37706     
37707     /**
37708      * Called after drag operations to set the size of the resizing element.
37709      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37710      * @param {Number} newSize The new size to set
37711      * @param {Function} onComplete A function to be invoked when resizing is complete
37712      */
37713     setElementSize : function(s, newSize, onComplete){
37714         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37715             if(!s.animate){
37716                 s.resizingEl.setWidth(newSize);
37717                 if(onComplete){
37718                     onComplete(s, newSize);
37719                 }
37720             }else{
37721                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37722             }
37723         }else{
37724             
37725             if(!s.animate){
37726                 s.resizingEl.setHeight(newSize);
37727                 if(onComplete){
37728                     onComplete(s, newSize);
37729                 }
37730             }else{
37731                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37732             }
37733         }
37734     }
37735 };
37736
37737 /** 
37738  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37739  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37740  * Adapter that  moves the splitter element to align with the resized sizing element. 
37741  * Used with an absolute positioned SplitBar.
37742  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37743  * document.body, make sure you assign an id to the body element.
37744  */
37745 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37746     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37747     this.container = Roo.get(container);
37748 };
37749
37750 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37751     init : function(s){
37752         this.basic.init(s);
37753     },
37754     
37755     getElementSize : function(s){
37756         return this.basic.getElementSize(s);
37757     },
37758     
37759     setElementSize : function(s, newSize, onComplete){
37760         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37761     },
37762     
37763     moveSplitter : function(s){
37764         var yes = Roo.bootstrap.SplitBar;
37765         switch(s.placement){
37766             case yes.LEFT:
37767                 s.el.setX(s.resizingEl.getRight());
37768                 break;
37769             case yes.RIGHT:
37770                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37771                 break;
37772             case yes.TOP:
37773                 s.el.setY(s.resizingEl.getBottom());
37774                 break;
37775             case yes.BOTTOM:
37776                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37777                 break;
37778         }
37779     }
37780 };
37781
37782 /**
37783  * Orientation constant - Create a vertical SplitBar
37784  * @static
37785  * @type Number
37786  */
37787 Roo.bootstrap.SplitBar.VERTICAL = 1;
37788
37789 /**
37790  * Orientation constant - Create a horizontal SplitBar
37791  * @static
37792  * @type Number
37793  */
37794 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37795
37796 /**
37797  * Placement constant - The resizing element is to the left of the splitter element
37798  * @static
37799  * @type Number
37800  */
37801 Roo.bootstrap.SplitBar.LEFT = 1;
37802
37803 /**
37804  * Placement constant - The resizing element is to the right of the splitter element
37805  * @static
37806  * @type Number
37807  */
37808 Roo.bootstrap.SplitBar.RIGHT = 2;
37809
37810 /**
37811  * Placement constant - The resizing element is positioned above the splitter element
37812  * @static
37813  * @type Number
37814  */
37815 Roo.bootstrap.SplitBar.TOP = 3;
37816
37817 /**
37818  * Placement constant - The resizing element is positioned under splitter element
37819  * @static
37820  * @type Number
37821  */
37822 Roo.bootstrap.SplitBar.BOTTOM = 4;
37823 /*
37824  * Based on:
37825  * Ext JS Library 1.1.1
37826  * Copyright(c) 2006-2007, Ext JS, LLC.
37827  *
37828  * Originally Released Under LGPL - original licence link has changed is not relivant.
37829  *
37830  * Fork - LGPL
37831  * <script type="text/javascript">
37832  */
37833
37834 /**
37835  * @class Roo.bootstrap.layout.Manager
37836  * @extends Roo.bootstrap.Component
37837  * @abstract
37838  * Base class for layout managers.
37839  */
37840 Roo.bootstrap.layout.Manager = function(config)
37841 {
37842     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37843
37844
37845
37846
37847
37848     /** false to disable window resize monitoring @type Boolean */
37849     this.monitorWindowResize = true;
37850     this.regions = {};
37851     this.addEvents({
37852         /**
37853          * @event layout
37854          * Fires when a layout is performed.
37855          * @param {Roo.LayoutManager} this
37856          */
37857         "layout" : true,
37858         /**
37859          * @event regionresized
37860          * Fires when the user resizes a region.
37861          * @param {Roo.LayoutRegion} region The resized region
37862          * @param {Number} newSize The new size (width for east/west, height for north/south)
37863          */
37864         "regionresized" : true,
37865         /**
37866          * @event regioncollapsed
37867          * Fires when a region is collapsed.
37868          * @param {Roo.LayoutRegion} region The collapsed region
37869          */
37870         "regioncollapsed" : true,
37871         /**
37872          * @event regionexpanded
37873          * Fires when a region is expanded.
37874          * @param {Roo.LayoutRegion} region The expanded region
37875          */
37876         "regionexpanded" : true
37877     });
37878     this.updating = false;
37879
37880     if (config.el) {
37881         this.el = Roo.get(config.el);
37882         this.initEvents();
37883     }
37884
37885 };
37886
37887 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37888
37889
37890     regions : null,
37891
37892     monitorWindowResize : true,
37893
37894
37895     updating : false,
37896
37897
37898     onRender : function(ct, position)
37899     {
37900         if(!this.el){
37901             this.el = Roo.get(ct);
37902             this.initEvents();
37903         }
37904         //this.fireEvent('render',this);
37905     },
37906
37907
37908     initEvents: function()
37909     {
37910
37911
37912         // ie scrollbar fix
37913         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37914             document.body.scroll = "no";
37915         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37916             this.el.position('relative');
37917         }
37918         this.id = this.el.id;
37919         this.el.addClass("roo-layout-container");
37920         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37921         if(this.el.dom != document.body ) {
37922             this.el.on('resize', this.layout,this);
37923             this.el.on('show', this.layout,this);
37924         }
37925
37926     },
37927
37928     /**
37929      * Returns true if this layout is currently being updated
37930      * @return {Boolean}
37931      */
37932     isUpdating : function(){
37933         return this.updating;
37934     },
37935
37936     /**
37937      * Suspend the LayoutManager from doing auto-layouts while
37938      * making multiple add or remove calls
37939      */
37940     beginUpdate : function(){
37941         this.updating = true;
37942     },
37943
37944     /**
37945      * Restore auto-layouts and optionally disable the manager from performing a layout
37946      * @param {Boolean} noLayout true to disable a layout update
37947      */
37948     endUpdate : function(noLayout){
37949         this.updating = false;
37950         if(!noLayout){
37951             this.layout();
37952         }
37953     },
37954
37955     layout: function(){
37956         // abstract...
37957     },
37958
37959     onRegionResized : function(region, newSize){
37960         this.fireEvent("regionresized", region, newSize);
37961         this.layout();
37962     },
37963
37964     onRegionCollapsed : function(region){
37965         this.fireEvent("regioncollapsed", region);
37966     },
37967
37968     onRegionExpanded : function(region){
37969         this.fireEvent("regionexpanded", region);
37970     },
37971
37972     /**
37973      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37974      * performs box-model adjustments.
37975      * @return {Object} The size as an object {width: (the width), height: (the height)}
37976      */
37977     getViewSize : function()
37978     {
37979         var size;
37980         if(this.el.dom != document.body){
37981             size = this.el.getSize();
37982         }else{
37983             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37984         }
37985         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37986         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37987         return size;
37988     },
37989
37990     /**
37991      * Returns the Element this layout is bound to.
37992      * @return {Roo.Element}
37993      */
37994     getEl : function(){
37995         return this.el;
37996     },
37997
37998     /**
37999      * Returns the specified region.
38000      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38001      * @return {Roo.LayoutRegion}
38002      */
38003     getRegion : function(target){
38004         return this.regions[target.toLowerCase()];
38005     },
38006
38007     onWindowResize : function(){
38008         if(this.monitorWindowResize){
38009             this.layout();
38010         }
38011     }
38012 });
38013 /*
38014  * Based on:
38015  * Ext JS Library 1.1.1
38016  * Copyright(c) 2006-2007, Ext JS, LLC.
38017  *
38018  * Originally Released Under LGPL - original licence link has changed is not relivant.
38019  *
38020  * Fork - LGPL
38021  * <script type="text/javascript">
38022  */
38023 /**
38024  * @class Roo.bootstrap.layout.Border
38025  * @extends Roo.bootstrap.layout.Manager
38026  * @builder-top
38027  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38028  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38029  * please see: examples/bootstrap/nested.html<br><br>
38030  
38031 <b>The container the layout is rendered into can be either the body element or any other element.
38032 If it is not the body element, the container needs to either be an absolute positioned element,
38033 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38034 the container size if it is not the body element.</b>
38035
38036 * @constructor
38037 * Create a new Border
38038 * @param {Object} config Configuration options
38039  */
38040 Roo.bootstrap.layout.Border = function(config){
38041     config = config || {};
38042     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38043     
38044     
38045     
38046     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38047         if(config[region]){
38048             config[region].region = region;
38049             this.addRegion(config[region]);
38050         }
38051     },this);
38052     
38053 };
38054
38055 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38056
38057 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38058     
38059         /**
38060          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38061          */
38062         /**
38063          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38064          */
38065         /**
38066          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38067          */
38068         /**
38069          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38070          */
38071         /**
38072          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38073          */
38074         
38075         
38076         
38077         
38078     parent : false, // this might point to a 'nest' or a ???
38079     
38080     /**
38081      * Creates and adds a new region if it doesn't already exist.
38082      * @param {String} target The target region key (north, south, east, west or center).
38083      * @param {Object} config The regions config object
38084      * @return {BorderLayoutRegion} The new region
38085      */
38086     addRegion : function(config)
38087     {
38088         if(!this.regions[config.region]){
38089             var r = this.factory(config);
38090             this.bindRegion(r);
38091         }
38092         return this.regions[config.region];
38093     },
38094
38095     // private (kinda)
38096     bindRegion : function(r){
38097         this.regions[r.config.region] = r;
38098         
38099         r.on("visibilitychange",    this.layout, this);
38100         r.on("paneladded",          this.layout, this);
38101         r.on("panelremoved",        this.layout, this);
38102         r.on("invalidated",         this.layout, this);
38103         r.on("resized",             this.onRegionResized, this);
38104         r.on("collapsed",           this.onRegionCollapsed, this);
38105         r.on("expanded",            this.onRegionExpanded, this);
38106     },
38107
38108     /**
38109      * Performs a layout update.
38110      */
38111     layout : function()
38112     {
38113         if(this.updating) {
38114             return;
38115         }
38116         
38117         // render all the rebions if they have not been done alreayd?
38118         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38119             if(this.regions[region] && !this.regions[region].bodyEl){
38120                 this.regions[region].onRender(this.el)
38121             }
38122         },this);
38123         
38124         var size = this.getViewSize();
38125         var w = size.width;
38126         var h = size.height;
38127         var centerW = w;
38128         var centerH = h;
38129         var centerY = 0;
38130         var centerX = 0;
38131         //var x = 0, y = 0;
38132
38133         var rs = this.regions;
38134         var north = rs["north"];
38135         var south = rs["south"]; 
38136         var west = rs["west"];
38137         var east = rs["east"];
38138         var center = rs["center"];
38139         //if(this.hideOnLayout){ // not supported anymore
38140             //c.el.setStyle("display", "none");
38141         //}
38142         if(north && north.isVisible()){
38143             var b = north.getBox();
38144             var m = north.getMargins();
38145             b.width = w - (m.left+m.right);
38146             b.x = m.left;
38147             b.y = m.top;
38148             centerY = b.height + b.y + m.bottom;
38149             centerH -= centerY;
38150             north.updateBox(this.safeBox(b));
38151         }
38152         if(south && south.isVisible()){
38153             var b = south.getBox();
38154             var m = south.getMargins();
38155             b.width = w - (m.left+m.right);
38156             b.x = m.left;
38157             var totalHeight = (b.height + m.top + m.bottom);
38158             b.y = h - totalHeight + m.top;
38159             centerH -= totalHeight;
38160             south.updateBox(this.safeBox(b));
38161         }
38162         if(west && west.isVisible()){
38163             var b = west.getBox();
38164             var m = west.getMargins();
38165             b.height = centerH - (m.top+m.bottom);
38166             b.x = m.left;
38167             b.y = centerY + m.top;
38168             var totalWidth = (b.width + m.left + m.right);
38169             centerX += totalWidth;
38170             centerW -= totalWidth;
38171             west.updateBox(this.safeBox(b));
38172         }
38173         if(east && east.isVisible()){
38174             var b = east.getBox();
38175             var m = east.getMargins();
38176             b.height = centerH - (m.top+m.bottom);
38177             var totalWidth = (b.width + m.left + m.right);
38178             b.x = w - totalWidth + m.left;
38179             b.y = centerY + m.top;
38180             centerW -= totalWidth;
38181             east.updateBox(this.safeBox(b));
38182         }
38183         if(center){
38184             var m = center.getMargins();
38185             var centerBox = {
38186                 x: centerX + m.left,
38187                 y: centerY + m.top,
38188                 width: centerW - (m.left+m.right),
38189                 height: centerH - (m.top+m.bottom)
38190             };
38191             //if(this.hideOnLayout){
38192                 //center.el.setStyle("display", "block");
38193             //}
38194             center.updateBox(this.safeBox(centerBox));
38195         }
38196         this.el.repaint();
38197         this.fireEvent("layout", this);
38198     },
38199
38200     // private
38201     safeBox : function(box){
38202         box.width = Math.max(0, box.width);
38203         box.height = Math.max(0, box.height);
38204         return box;
38205     },
38206
38207     /**
38208      * Adds a ContentPanel (or subclass) to this layout.
38209      * @param {String} target The target region key (north, south, east, west or center).
38210      * @param {Roo.ContentPanel} panel The panel to add
38211      * @return {Roo.ContentPanel} The added panel
38212      */
38213     add : function(target, panel){
38214          
38215         target = target.toLowerCase();
38216         return this.regions[target].add(panel);
38217     },
38218
38219     /**
38220      * Remove a ContentPanel (or subclass) to this layout.
38221      * @param {String} target The target region key (north, south, east, west or center).
38222      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38223      * @return {Roo.ContentPanel} The removed panel
38224      */
38225     remove : function(target, panel){
38226         target = target.toLowerCase();
38227         return this.regions[target].remove(panel);
38228     },
38229
38230     /**
38231      * Searches all regions for a panel with the specified id
38232      * @param {String} panelId
38233      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38234      */
38235     findPanel : function(panelId){
38236         var rs = this.regions;
38237         for(var target in rs){
38238             if(typeof rs[target] != "function"){
38239                 var p = rs[target].getPanel(panelId);
38240                 if(p){
38241                     return p;
38242                 }
38243             }
38244         }
38245         return null;
38246     },
38247
38248     /**
38249      * Searches all regions for a panel with the specified id and activates (shows) it.
38250      * @param {String/ContentPanel} panelId The panels id or the panel itself
38251      * @return {Roo.ContentPanel} The shown panel or null
38252      */
38253     showPanel : function(panelId) {
38254       var rs = this.regions;
38255       for(var target in rs){
38256          var r = rs[target];
38257          if(typeof r != "function"){
38258             if(r.hasPanel(panelId)){
38259                return r.showPanel(panelId);
38260             }
38261          }
38262       }
38263       return null;
38264    },
38265
38266    /**
38267      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38268      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38269      */
38270    /*
38271     restoreState : function(provider){
38272         if(!provider){
38273             provider = Roo.state.Manager;
38274         }
38275         var sm = new Roo.LayoutStateManager();
38276         sm.init(this, provider);
38277     },
38278 */
38279  
38280  
38281     /**
38282      * Adds a xtype elements to the layout.
38283      * <pre><code>
38284
38285 layout.addxtype({
38286        xtype : 'ContentPanel',
38287        region: 'west',
38288        items: [ .... ]
38289    }
38290 );
38291
38292 layout.addxtype({
38293         xtype : 'NestedLayoutPanel',
38294         region: 'west',
38295         layout: {
38296            center: { },
38297            west: { }   
38298         },
38299         items : [ ... list of content panels or nested layout panels.. ]
38300    }
38301 );
38302 </code></pre>
38303      * @param {Object} cfg Xtype definition of item to add.
38304      */
38305     addxtype : function(cfg)
38306     {
38307         // basically accepts a pannel...
38308         // can accept a layout region..!?!?
38309         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38310         
38311         
38312         // theory?  children can only be panels??
38313         
38314         //if (!cfg.xtype.match(/Panel$/)) {
38315         //    return false;
38316         //}
38317         var ret = false;
38318         
38319         if (typeof(cfg.region) == 'undefined') {
38320             Roo.log("Failed to add Panel, region was not set");
38321             Roo.log(cfg);
38322             return false;
38323         }
38324         var region = cfg.region;
38325         delete cfg.region;
38326         
38327           
38328         var xitems = [];
38329         if (cfg.items) {
38330             xitems = cfg.items;
38331             delete cfg.items;
38332         }
38333         var nb = false;
38334         
38335         if ( region == 'center') {
38336             Roo.log("Center: " + cfg.title);
38337         }
38338         
38339         
38340         switch(cfg.xtype) 
38341         {
38342             case 'Content':  // ContentPanel (el, cfg)
38343             case 'Scroll':  // ContentPanel (el, cfg)
38344             case 'View': 
38345                 cfg.autoCreate = cfg.autoCreate || true;
38346                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38347                 //} else {
38348                 //    var el = this.el.createChild();
38349                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38350                 //}
38351                 
38352                 this.add(region, ret);
38353                 break;
38354             
38355             /*
38356             case 'TreePanel': // our new panel!
38357                 cfg.el = this.el.createChild();
38358                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38359                 this.add(region, ret);
38360                 break;
38361             */
38362             
38363             case 'Nest': 
38364                 // create a new Layout (which is  a Border Layout...
38365                 
38366                 var clayout = cfg.layout;
38367                 clayout.el  = this.el.createChild();
38368                 clayout.items   = clayout.items  || [];
38369                 
38370                 delete cfg.layout;
38371                 
38372                 // replace this exitems with the clayout ones..
38373                 xitems = clayout.items;
38374                  
38375                 // force background off if it's in center...
38376                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38377                     cfg.background = false;
38378                 }
38379                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38380                 
38381                 
38382                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38383                 //console.log('adding nested layout panel '  + cfg.toSource());
38384                 this.add(region, ret);
38385                 nb = {}; /// find first...
38386                 break;
38387             
38388             case 'Grid':
38389                 
38390                 // needs grid and region
38391                 
38392                 //var el = this.getRegion(region).el.createChild();
38393                 /*
38394                  *var el = this.el.createChild();
38395                 // create the grid first...
38396                 cfg.grid.container = el;
38397                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38398                 */
38399                 
38400                 if (region == 'center' && this.active ) {
38401                     cfg.background = false;
38402                 }
38403                 
38404                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38405                 
38406                 this.add(region, ret);
38407                 /*
38408                 if (cfg.background) {
38409                     // render grid on panel activation (if panel background)
38410                     ret.on('activate', function(gp) {
38411                         if (!gp.grid.rendered) {
38412                     //        gp.grid.render(el);
38413                         }
38414                     });
38415                 } else {
38416                   //  cfg.grid.render(el);
38417                 }
38418                 */
38419                 break;
38420            
38421            
38422             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38423                 // it was the old xcomponent building that caused this before.
38424                 // espeically if border is the top element in the tree.
38425                 ret = this;
38426                 break; 
38427                 
38428                     
38429                 
38430                 
38431                 
38432             default:
38433                 /*
38434                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38435                     
38436                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38437                     this.add(region, ret);
38438                 } else {
38439                 */
38440                     Roo.log(cfg);
38441                     throw "Can not add '" + cfg.xtype + "' to Border";
38442                     return null;
38443              
38444                                 
38445              
38446         }
38447         this.beginUpdate();
38448         // add children..
38449         var region = '';
38450         var abn = {};
38451         Roo.each(xitems, function(i)  {
38452             region = nb && i.region ? i.region : false;
38453             
38454             var add = ret.addxtype(i);
38455            
38456             if (region) {
38457                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38458                 if (!i.background) {
38459                     abn[region] = nb[region] ;
38460                 }
38461             }
38462             
38463         });
38464         this.endUpdate();
38465
38466         // make the last non-background panel active..
38467         //if (nb) { Roo.log(abn); }
38468         if (nb) {
38469             
38470             for(var r in abn) {
38471                 region = this.getRegion(r);
38472                 if (region) {
38473                     // tried using nb[r], but it does not work..
38474                      
38475                     region.showPanel(abn[r]);
38476                    
38477                 }
38478             }
38479         }
38480         return ret;
38481         
38482     },
38483     
38484     
38485 // private
38486     factory : function(cfg)
38487     {
38488         
38489         var validRegions = Roo.bootstrap.layout.Border.regions;
38490
38491         var target = cfg.region;
38492         cfg.mgr = this;
38493         
38494         var r = Roo.bootstrap.layout;
38495         Roo.log(target);
38496         switch(target){
38497             case "north":
38498                 return new r.North(cfg);
38499             case "south":
38500                 return new r.South(cfg);
38501             case "east":
38502                 return new r.East(cfg);
38503             case "west":
38504                 return new r.West(cfg);
38505             case "center":
38506                 return new r.Center(cfg);
38507         }
38508         throw 'Layout region "'+target+'" not supported.';
38509     }
38510     
38511     
38512 });
38513  /*
38514  * Based on:
38515  * Ext JS Library 1.1.1
38516  * Copyright(c) 2006-2007, Ext JS, LLC.
38517  *
38518  * Originally Released Under LGPL - original licence link has changed is not relivant.
38519  *
38520  * Fork - LGPL
38521  * <script type="text/javascript">
38522  */
38523  
38524 /**
38525  * @class Roo.bootstrap.layout.Basic
38526  * @extends Roo.util.Observable
38527  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38528  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38529  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38530  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38531  * @cfg {string}   region  the region that it inhabits..
38532  * @cfg {bool}   skipConfig skip config?
38533  * 
38534
38535  */
38536 Roo.bootstrap.layout.Basic = function(config){
38537     
38538     this.mgr = config.mgr;
38539     
38540     this.position = config.region;
38541     
38542     var skipConfig = config.skipConfig;
38543     
38544     this.events = {
38545         /**
38546          * @scope Roo.BasicLayoutRegion
38547          */
38548         
38549         /**
38550          * @event beforeremove
38551          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38552          * @param {Roo.LayoutRegion} this
38553          * @param {Roo.ContentPanel} panel The panel
38554          * @param {Object} e The cancel event object
38555          */
38556         "beforeremove" : true,
38557         /**
38558          * @event invalidated
38559          * Fires when the layout for this region is changed.
38560          * @param {Roo.LayoutRegion} this
38561          */
38562         "invalidated" : true,
38563         /**
38564          * @event visibilitychange
38565          * Fires when this region is shown or hidden 
38566          * @param {Roo.LayoutRegion} this
38567          * @param {Boolean} visibility true or false
38568          */
38569         "visibilitychange" : true,
38570         /**
38571          * @event paneladded
38572          * Fires when a panel is added. 
38573          * @param {Roo.LayoutRegion} this
38574          * @param {Roo.ContentPanel} panel The panel
38575          */
38576         "paneladded" : true,
38577         /**
38578          * @event panelremoved
38579          * Fires when a panel is removed. 
38580          * @param {Roo.LayoutRegion} this
38581          * @param {Roo.ContentPanel} panel The panel
38582          */
38583         "panelremoved" : true,
38584         /**
38585          * @event beforecollapse
38586          * Fires when this region before collapse.
38587          * @param {Roo.LayoutRegion} this
38588          */
38589         "beforecollapse" : true,
38590         /**
38591          * @event collapsed
38592          * Fires when this region is collapsed.
38593          * @param {Roo.LayoutRegion} this
38594          */
38595         "collapsed" : true,
38596         /**
38597          * @event expanded
38598          * Fires when this region is expanded.
38599          * @param {Roo.LayoutRegion} this
38600          */
38601         "expanded" : true,
38602         /**
38603          * @event slideshow
38604          * Fires when this region is slid into view.
38605          * @param {Roo.LayoutRegion} this
38606          */
38607         "slideshow" : true,
38608         /**
38609          * @event slidehide
38610          * Fires when this region slides out of view. 
38611          * @param {Roo.LayoutRegion} this
38612          */
38613         "slidehide" : true,
38614         /**
38615          * @event panelactivated
38616          * Fires when a panel is activated. 
38617          * @param {Roo.LayoutRegion} this
38618          * @param {Roo.ContentPanel} panel The activated panel
38619          */
38620         "panelactivated" : true,
38621         /**
38622          * @event resized
38623          * Fires when the user resizes this region. 
38624          * @param {Roo.LayoutRegion} this
38625          * @param {Number} newSize The new size (width for east/west, height for north/south)
38626          */
38627         "resized" : true
38628     };
38629     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38630     this.panels = new Roo.util.MixedCollection();
38631     this.panels.getKey = this.getPanelId.createDelegate(this);
38632     this.box = null;
38633     this.activePanel = null;
38634     // ensure listeners are added...
38635     
38636     if (config.listeners || config.events) {
38637         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38638             listeners : config.listeners || {},
38639             events : config.events || {}
38640         });
38641     }
38642     
38643     if(skipConfig !== true){
38644         this.applyConfig(config);
38645     }
38646 };
38647
38648 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38649 {
38650     getPanelId : function(p){
38651         return p.getId();
38652     },
38653     
38654     applyConfig : function(config){
38655         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38656         this.config = config;
38657         
38658     },
38659     
38660     /**
38661      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38662      * the width, for horizontal (north, south) the height.
38663      * @param {Number} newSize The new width or height
38664      */
38665     resizeTo : function(newSize){
38666         var el = this.el ? this.el :
38667                  (this.activePanel ? this.activePanel.getEl() : null);
38668         if(el){
38669             switch(this.position){
38670                 case "east":
38671                 case "west":
38672                     el.setWidth(newSize);
38673                     this.fireEvent("resized", this, newSize);
38674                 break;
38675                 case "north":
38676                 case "south":
38677                     el.setHeight(newSize);
38678                     this.fireEvent("resized", this, newSize);
38679                 break;                
38680             }
38681         }
38682     },
38683     
38684     getBox : function(){
38685         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38686     },
38687     
38688     getMargins : function(){
38689         return this.margins;
38690     },
38691     
38692     updateBox : function(box){
38693         this.box = box;
38694         var el = this.activePanel.getEl();
38695         el.dom.style.left = box.x + "px";
38696         el.dom.style.top = box.y + "px";
38697         this.activePanel.setSize(box.width, box.height);
38698     },
38699     
38700     /**
38701      * Returns the container element for this region.
38702      * @return {Roo.Element}
38703      */
38704     getEl : function(){
38705         return this.activePanel;
38706     },
38707     
38708     /**
38709      * Returns true if this region is currently visible.
38710      * @return {Boolean}
38711      */
38712     isVisible : function(){
38713         return this.activePanel ? true : false;
38714     },
38715     
38716     setActivePanel : function(panel){
38717         panel = this.getPanel(panel);
38718         if(this.activePanel && this.activePanel != panel){
38719             this.activePanel.setActiveState(false);
38720             this.activePanel.getEl().setLeftTop(-10000,-10000);
38721         }
38722         this.activePanel = panel;
38723         panel.setActiveState(true);
38724         if(this.box){
38725             panel.setSize(this.box.width, this.box.height);
38726         }
38727         this.fireEvent("panelactivated", this, panel);
38728         this.fireEvent("invalidated");
38729     },
38730     
38731     /**
38732      * Show the specified panel.
38733      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38734      * @return {Roo.ContentPanel} The shown panel or null
38735      */
38736     showPanel : function(panel){
38737         panel = this.getPanel(panel);
38738         if(panel){
38739             this.setActivePanel(panel);
38740         }
38741         return panel;
38742     },
38743     
38744     /**
38745      * Get the active panel for this region.
38746      * @return {Roo.ContentPanel} The active panel or null
38747      */
38748     getActivePanel : function(){
38749         return this.activePanel;
38750     },
38751     
38752     /**
38753      * Add the passed ContentPanel(s)
38754      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38755      * @return {Roo.ContentPanel} The panel added (if only one was added)
38756      */
38757     add : function(panel){
38758         if(arguments.length > 1){
38759             for(var i = 0, len = arguments.length; i < len; i++) {
38760                 this.add(arguments[i]);
38761             }
38762             return null;
38763         }
38764         if(this.hasPanel(panel)){
38765             this.showPanel(panel);
38766             return panel;
38767         }
38768         var el = panel.getEl();
38769         if(el.dom.parentNode != this.mgr.el.dom){
38770             this.mgr.el.dom.appendChild(el.dom);
38771         }
38772         if(panel.setRegion){
38773             panel.setRegion(this);
38774         }
38775         this.panels.add(panel);
38776         el.setStyle("position", "absolute");
38777         if(!panel.background){
38778             this.setActivePanel(panel);
38779             if(this.config.initialSize && this.panels.getCount()==1){
38780                 this.resizeTo(this.config.initialSize);
38781             }
38782         }
38783         this.fireEvent("paneladded", this, panel);
38784         return panel;
38785     },
38786     
38787     /**
38788      * Returns true if the panel is in this region.
38789      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38790      * @return {Boolean}
38791      */
38792     hasPanel : function(panel){
38793         if(typeof panel == "object"){ // must be panel obj
38794             panel = panel.getId();
38795         }
38796         return this.getPanel(panel) ? true : false;
38797     },
38798     
38799     /**
38800      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38801      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38802      * @param {Boolean} preservePanel Overrides the config preservePanel option
38803      * @return {Roo.ContentPanel} The panel that was removed
38804      */
38805     remove : function(panel, preservePanel){
38806         panel = this.getPanel(panel);
38807         if(!panel){
38808             return null;
38809         }
38810         var e = {};
38811         this.fireEvent("beforeremove", this, panel, e);
38812         if(e.cancel === true){
38813             return null;
38814         }
38815         var panelId = panel.getId();
38816         this.panels.removeKey(panelId);
38817         return panel;
38818     },
38819     
38820     /**
38821      * Returns the panel specified or null if it's not in this region.
38822      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38823      * @return {Roo.ContentPanel}
38824      */
38825     getPanel : function(id){
38826         if(typeof id == "object"){ // must be panel obj
38827             return id;
38828         }
38829         return this.panels.get(id);
38830     },
38831     
38832     /**
38833      * Returns this regions position (north/south/east/west/center).
38834      * @return {String} 
38835      */
38836     getPosition: function(){
38837         return this.position;    
38838     }
38839 });/*
38840  * Based on:
38841  * Ext JS Library 1.1.1
38842  * Copyright(c) 2006-2007, Ext JS, LLC.
38843  *
38844  * Originally Released Under LGPL - original licence link has changed is not relivant.
38845  *
38846  * Fork - LGPL
38847  * <script type="text/javascript">
38848  */
38849  
38850 /**
38851  * @class Roo.bootstrap.layout.Region
38852  * @extends Roo.bootstrap.layout.Basic
38853  * This class represents a region in a layout manager.
38854  
38855  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38856  * @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})
38857  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38858  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38859  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38860  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38861  * @cfg {String}    title           The title for the region (overrides panel titles)
38862  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38863  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38864  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38865  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38866  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38867  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38868  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38869  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38870  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38871  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38872
38873  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38874  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38875  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38876  * @cfg {Number}    width           For East/West panels
38877  * @cfg {Number}    height          For North/South panels
38878  * @cfg {Boolean}   split           To show the splitter
38879  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38880  * 
38881  * @cfg {string}   cls             Extra CSS classes to add to region
38882  * 
38883  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38884  * @cfg {string}   region  the region that it inhabits..
38885  *
38886
38887  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38888  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38889
38890  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38891  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38892  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38893  */
38894 Roo.bootstrap.layout.Region = function(config)
38895 {
38896     this.applyConfig(config);
38897
38898     var mgr = config.mgr;
38899     var pos = config.region;
38900     config.skipConfig = true;
38901     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38902     
38903     if (mgr.el) {
38904         this.onRender(mgr.el);   
38905     }
38906      
38907     this.visible = true;
38908     this.collapsed = false;
38909     this.unrendered_panels = [];
38910 };
38911
38912 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38913
38914     position: '', // set by wrapper (eg. north/south etc..)
38915     unrendered_panels : null,  // unrendered panels.
38916     
38917     tabPosition : false,
38918     
38919     mgr: false, // points to 'Border'
38920     
38921     
38922     createBody : function(){
38923         /** This region's body element 
38924         * @type Roo.Element */
38925         this.bodyEl = this.el.createChild({
38926                 tag: "div",
38927                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38928         });
38929     },
38930
38931     onRender: function(ctr, pos)
38932     {
38933         var dh = Roo.DomHelper;
38934         /** This region's container element 
38935         * @type Roo.Element */
38936         this.el = dh.append(ctr.dom, {
38937                 tag: "div",
38938                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38939             }, true);
38940         /** This region's title element 
38941         * @type Roo.Element */
38942     
38943         this.titleEl = dh.append(this.el.dom,  {
38944                 tag: "div",
38945                 unselectable: "on",
38946                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38947                 children:[
38948                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38949                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38950                 ]
38951             }, true);
38952         
38953         this.titleEl.enableDisplayMode();
38954         /** This region's title text element 
38955         * @type HTMLElement */
38956         this.titleTextEl = this.titleEl.dom.firstChild;
38957         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38958         /*
38959         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38960         this.closeBtn.enableDisplayMode();
38961         this.closeBtn.on("click", this.closeClicked, this);
38962         this.closeBtn.hide();
38963     */
38964         this.createBody(this.config);
38965         if(this.config.hideWhenEmpty){
38966             this.hide();
38967             this.on("paneladded", this.validateVisibility, this);
38968             this.on("panelremoved", this.validateVisibility, this);
38969         }
38970         if(this.autoScroll){
38971             this.bodyEl.setStyle("overflow", "auto");
38972         }else{
38973             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38974         }
38975         //if(c.titlebar !== false){
38976             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38977                 this.titleEl.hide();
38978             }else{
38979                 this.titleEl.show();
38980                 if(this.config.title){
38981                     this.titleTextEl.innerHTML = this.config.title;
38982                 }
38983             }
38984         //}
38985         if(this.config.collapsed){
38986             this.collapse(true);
38987         }
38988         if(this.config.hidden){
38989             this.hide();
38990         }
38991         
38992         if (this.unrendered_panels && this.unrendered_panels.length) {
38993             for (var i =0;i< this.unrendered_panels.length; i++) {
38994                 this.add(this.unrendered_panels[i]);
38995             }
38996             this.unrendered_panels = null;
38997             
38998         }
38999         
39000     },
39001     
39002     applyConfig : function(c)
39003     {
39004         /*
39005          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39006             var dh = Roo.DomHelper;
39007             if(c.titlebar !== false){
39008                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39009                 this.collapseBtn.on("click", this.collapse, this);
39010                 this.collapseBtn.enableDisplayMode();
39011                 /*
39012                 if(c.showPin === true || this.showPin){
39013                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39014                     this.stickBtn.enableDisplayMode();
39015                     this.stickBtn.on("click", this.expand, this);
39016                     this.stickBtn.hide();
39017                 }
39018                 
39019             }
39020             */
39021             /** This region's collapsed element
39022             * @type Roo.Element */
39023             /*
39024              *
39025             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39026                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39027             ]}, true);
39028             
39029             if(c.floatable !== false){
39030                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39031                this.collapsedEl.on("click", this.collapseClick, this);
39032             }
39033
39034             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39035                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39036                    id: "message", unselectable: "on", style:{"float":"left"}});
39037                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39038              }
39039             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39040             this.expandBtn.on("click", this.expand, this);
39041             
39042         }
39043         
39044         if(this.collapseBtn){
39045             this.collapseBtn.setVisible(c.collapsible == true);
39046         }
39047         
39048         this.cmargins = c.cmargins || this.cmargins ||
39049                          (this.position == "west" || this.position == "east" ?
39050                              {top: 0, left: 2, right:2, bottom: 0} :
39051                              {top: 2, left: 0, right:0, bottom: 2});
39052         */
39053         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39054         
39055         
39056         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39057         
39058         this.autoScroll = c.autoScroll || false;
39059         
39060         
39061        
39062         
39063         this.duration = c.duration || .30;
39064         this.slideDuration = c.slideDuration || .45;
39065         this.config = c;
39066        
39067     },
39068     /**
39069      * Returns true if this region is currently visible.
39070      * @return {Boolean}
39071      */
39072     isVisible : function(){
39073         return this.visible;
39074     },
39075
39076     /**
39077      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39078      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39079      */
39080     //setCollapsedTitle : function(title){
39081     //    title = title || "&#160;";
39082      //   if(this.collapsedTitleTextEl){
39083       //      this.collapsedTitleTextEl.innerHTML = title;
39084        // }
39085     //},
39086
39087     getBox : function(){
39088         var b;
39089       //  if(!this.collapsed){
39090             b = this.el.getBox(false, true);
39091        // }else{
39092           //  b = this.collapsedEl.getBox(false, true);
39093         //}
39094         return b;
39095     },
39096
39097     getMargins : function(){
39098         return this.margins;
39099         //return this.collapsed ? this.cmargins : this.margins;
39100     },
39101 /*
39102     highlight : function(){
39103         this.el.addClass("x-layout-panel-dragover");
39104     },
39105
39106     unhighlight : function(){
39107         this.el.removeClass("x-layout-panel-dragover");
39108     },
39109 */
39110     updateBox : function(box)
39111     {
39112         if (!this.bodyEl) {
39113             return; // not rendered yet..
39114         }
39115         
39116         this.box = box;
39117         if(!this.collapsed){
39118             this.el.dom.style.left = box.x + "px";
39119             this.el.dom.style.top = box.y + "px";
39120             this.updateBody(box.width, box.height);
39121         }else{
39122             this.collapsedEl.dom.style.left = box.x + "px";
39123             this.collapsedEl.dom.style.top = box.y + "px";
39124             this.collapsedEl.setSize(box.width, box.height);
39125         }
39126         if(this.tabs){
39127             this.tabs.autoSizeTabs();
39128         }
39129     },
39130
39131     updateBody : function(w, h)
39132     {
39133         if(w !== null){
39134             this.el.setWidth(w);
39135             w -= this.el.getBorderWidth("rl");
39136             if(this.config.adjustments){
39137                 w += this.config.adjustments[0];
39138             }
39139         }
39140         if(h !== null && h > 0){
39141             this.el.setHeight(h);
39142             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39143             h -= this.el.getBorderWidth("tb");
39144             if(this.config.adjustments){
39145                 h += this.config.adjustments[1];
39146             }
39147             this.bodyEl.setHeight(h);
39148             if(this.tabs){
39149                 h = this.tabs.syncHeight(h);
39150             }
39151         }
39152         if(this.panelSize){
39153             w = w !== null ? w : this.panelSize.width;
39154             h = h !== null ? h : this.panelSize.height;
39155         }
39156         if(this.activePanel){
39157             var el = this.activePanel.getEl();
39158             w = w !== null ? w : el.getWidth();
39159             h = h !== null ? h : el.getHeight();
39160             this.panelSize = {width: w, height: h};
39161             this.activePanel.setSize(w, h);
39162         }
39163         if(Roo.isIE && this.tabs){
39164             this.tabs.el.repaint();
39165         }
39166     },
39167
39168     /**
39169      * Returns the container element for this region.
39170      * @return {Roo.Element}
39171      */
39172     getEl : function(){
39173         return this.el;
39174     },
39175
39176     /**
39177      * Hides this region.
39178      */
39179     hide : function(){
39180         //if(!this.collapsed){
39181             this.el.dom.style.left = "-2000px";
39182             this.el.hide();
39183         //}else{
39184          //   this.collapsedEl.dom.style.left = "-2000px";
39185          //   this.collapsedEl.hide();
39186        // }
39187         this.visible = false;
39188         this.fireEvent("visibilitychange", this, false);
39189     },
39190
39191     /**
39192      * Shows this region if it was previously hidden.
39193      */
39194     show : function(){
39195         //if(!this.collapsed){
39196             this.el.show();
39197         //}else{
39198         //    this.collapsedEl.show();
39199        // }
39200         this.visible = true;
39201         this.fireEvent("visibilitychange", this, true);
39202     },
39203 /*
39204     closeClicked : function(){
39205         if(this.activePanel){
39206             this.remove(this.activePanel);
39207         }
39208     },
39209
39210     collapseClick : function(e){
39211         if(this.isSlid){
39212            e.stopPropagation();
39213            this.slideIn();
39214         }else{
39215            e.stopPropagation();
39216            this.slideOut();
39217         }
39218     },
39219 */
39220     /**
39221      * Collapses this region.
39222      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39223      */
39224     /*
39225     collapse : function(skipAnim, skipCheck = false){
39226         if(this.collapsed) {
39227             return;
39228         }
39229         
39230         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39231             
39232             this.collapsed = true;
39233             if(this.split){
39234                 this.split.el.hide();
39235             }
39236             if(this.config.animate && skipAnim !== true){
39237                 this.fireEvent("invalidated", this);
39238                 this.animateCollapse();
39239             }else{
39240                 this.el.setLocation(-20000,-20000);
39241                 this.el.hide();
39242                 this.collapsedEl.show();
39243                 this.fireEvent("collapsed", this);
39244                 this.fireEvent("invalidated", this);
39245             }
39246         }
39247         
39248     },
39249 */
39250     animateCollapse : function(){
39251         // overridden
39252     },
39253
39254     /**
39255      * Expands this region if it was previously collapsed.
39256      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39257      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39258      */
39259     /*
39260     expand : function(e, skipAnim){
39261         if(e) {
39262             e.stopPropagation();
39263         }
39264         if(!this.collapsed || this.el.hasActiveFx()) {
39265             return;
39266         }
39267         if(this.isSlid){
39268             this.afterSlideIn();
39269             skipAnim = true;
39270         }
39271         this.collapsed = false;
39272         if(this.config.animate && skipAnim !== true){
39273             this.animateExpand();
39274         }else{
39275             this.el.show();
39276             if(this.split){
39277                 this.split.el.show();
39278             }
39279             this.collapsedEl.setLocation(-2000,-2000);
39280             this.collapsedEl.hide();
39281             this.fireEvent("invalidated", this);
39282             this.fireEvent("expanded", this);
39283         }
39284     },
39285 */
39286     animateExpand : function(){
39287         // overridden
39288     },
39289
39290     initTabs : function()
39291     {
39292         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39293         
39294         var ts = new Roo.bootstrap.panel.Tabs({
39295             el: this.bodyEl.dom,
39296             region : this,
39297             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39298             disableTooltips: this.config.disableTabTips,
39299             toolbar : this.config.toolbar
39300         });
39301         
39302         if(this.config.hideTabs){
39303             ts.stripWrap.setDisplayed(false);
39304         }
39305         this.tabs = ts;
39306         ts.resizeTabs = this.config.resizeTabs === true;
39307         ts.minTabWidth = this.config.minTabWidth || 40;
39308         ts.maxTabWidth = this.config.maxTabWidth || 250;
39309         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39310         ts.monitorResize = false;
39311         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39312         ts.bodyEl.addClass('roo-layout-tabs-body');
39313         this.panels.each(this.initPanelAsTab, this);
39314     },
39315
39316     initPanelAsTab : function(panel){
39317         var ti = this.tabs.addTab(
39318             panel.getEl().id,
39319             panel.getTitle(),
39320             null,
39321             this.config.closeOnTab && panel.isClosable(),
39322             panel.tpl
39323         );
39324         if(panel.tabTip !== undefined){
39325             ti.setTooltip(panel.tabTip);
39326         }
39327         ti.on("activate", function(){
39328               this.setActivePanel(panel);
39329         }, this);
39330         
39331         if(this.config.closeOnTab){
39332             ti.on("beforeclose", function(t, e){
39333                 e.cancel = true;
39334                 this.remove(panel);
39335             }, this);
39336         }
39337         
39338         panel.tabItem = ti;
39339         
39340         return ti;
39341     },
39342
39343     updatePanelTitle : function(panel, title)
39344     {
39345         if(this.activePanel == panel){
39346             this.updateTitle(title);
39347         }
39348         if(this.tabs){
39349             var ti = this.tabs.getTab(panel.getEl().id);
39350             ti.setText(title);
39351             if(panel.tabTip !== undefined){
39352                 ti.setTooltip(panel.tabTip);
39353             }
39354         }
39355     },
39356
39357     updateTitle : function(title){
39358         if(this.titleTextEl && !this.config.title){
39359             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39360         }
39361     },
39362
39363     setActivePanel : function(panel)
39364     {
39365         panel = this.getPanel(panel);
39366         if(this.activePanel && this.activePanel != panel){
39367             if(this.activePanel.setActiveState(false) === false){
39368                 return;
39369             }
39370         }
39371         this.activePanel = panel;
39372         panel.setActiveState(true);
39373         if(this.panelSize){
39374             panel.setSize(this.panelSize.width, this.panelSize.height);
39375         }
39376         if(this.closeBtn){
39377             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39378         }
39379         this.updateTitle(panel.getTitle());
39380         if(this.tabs){
39381             this.fireEvent("invalidated", this);
39382         }
39383         this.fireEvent("panelactivated", this, panel);
39384     },
39385
39386     /**
39387      * Shows the specified panel.
39388      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39389      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39390      */
39391     showPanel : function(panel)
39392     {
39393         panel = this.getPanel(panel);
39394         if(panel){
39395             if(this.tabs){
39396                 var tab = this.tabs.getTab(panel.getEl().id);
39397                 if(tab.isHidden()){
39398                     this.tabs.unhideTab(tab.id);
39399                 }
39400                 tab.activate();
39401             }else{
39402                 this.setActivePanel(panel);
39403             }
39404         }
39405         return panel;
39406     },
39407
39408     /**
39409      * Get the active panel for this region.
39410      * @return {Roo.ContentPanel} The active panel or null
39411      */
39412     getActivePanel : function(){
39413         return this.activePanel;
39414     },
39415
39416     validateVisibility : function(){
39417         if(this.panels.getCount() < 1){
39418             this.updateTitle("&#160;");
39419             this.closeBtn.hide();
39420             this.hide();
39421         }else{
39422             if(!this.isVisible()){
39423                 this.show();
39424             }
39425         }
39426     },
39427
39428     /**
39429      * Adds the passed ContentPanel(s) to this region.
39430      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39431      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39432      */
39433     add : function(panel)
39434     {
39435         if(arguments.length > 1){
39436             for(var i = 0, len = arguments.length; i < len; i++) {
39437                 this.add(arguments[i]);
39438             }
39439             return null;
39440         }
39441         
39442         // if we have not been rendered yet, then we can not really do much of this..
39443         if (!this.bodyEl) {
39444             this.unrendered_panels.push(panel);
39445             return panel;
39446         }
39447         
39448         
39449         
39450         
39451         if(this.hasPanel(panel)){
39452             this.showPanel(panel);
39453             return panel;
39454         }
39455         panel.setRegion(this);
39456         this.panels.add(panel);
39457        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39458             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39459             // and hide them... ???
39460             this.bodyEl.dom.appendChild(panel.getEl().dom);
39461             if(panel.background !== true){
39462                 this.setActivePanel(panel);
39463             }
39464             this.fireEvent("paneladded", this, panel);
39465             return panel;
39466         }
39467         */
39468         if(!this.tabs){
39469             this.initTabs();
39470         }else{
39471             this.initPanelAsTab(panel);
39472         }
39473         
39474         
39475         if(panel.background !== true){
39476             this.tabs.activate(panel.getEl().id);
39477         }
39478         this.fireEvent("paneladded", this, panel);
39479         return panel;
39480     },
39481
39482     /**
39483      * Hides the tab for the specified panel.
39484      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39485      */
39486     hidePanel : function(panel){
39487         if(this.tabs && (panel = this.getPanel(panel))){
39488             this.tabs.hideTab(panel.getEl().id);
39489         }
39490     },
39491
39492     /**
39493      * Unhides the tab for a previously hidden panel.
39494      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39495      */
39496     unhidePanel : function(panel){
39497         if(this.tabs && (panel = this.getPanel(panel))){
39498             this.tabs.unhideTab(panel.getEl().id);
39499         }
39500     },
39501
39502     clearPanels : function(){
39503         while(this.panels.getCount() > 0){
39504              this.remove(this.panels.first());
39505         }
39506     },
39507
39508     /**
39509      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39510      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39511      * @param {Boolean} preservePanel Overrides the config preservePanel option
39512      * @return {Roo.ContentPanel} The panel that was removed
39513      */
39514     remove : function(panel, preservePanel)
39515     {
39516         panel = this.getPanel(panel);
39517         if(!panel){
39518             return null;
39519         }
39520         var e = {};
39521         this.fireEvent("beforeremove", this, panel, e);
39522         if(e.cancel === true){
39523             return null;
39524         }
39525         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39526         var panelId = panel.getId();
39527         this.panels.removeKey(panelId);
39528         if(preservePanel){
39529             document.body.appendChild(panel.getEl().dom);
39530         }
39531         if(this.tabs){
39532             this.tabs.removeTab(panel.getEl().id);
39533         }else if (!preservePanel){
39534             this.bodyEl.dom.removeChild(panel.getEl().dom);
39535         }
39536         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39537             var p = this.panels.first();
39538             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39539             tempEl.appendChild(p.getEl().dom);
39540             this.bodyEl.update("");
39541             this.bodyEl.dom.appendChild(p.getEl().dom);
39542             tempEl = null;
39543             this.updateTitle(p.getTitle());
39544             this.tabs = null;
39545             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39546             this.setActivePanel(p);
39547         }
39548         panel.setRegion(null);
39549         if(this.activePanel == panel){
39550             this.activePanel = null;
39551         }
39552         if(this.config.autoDestroy !== false && preservePanel !== true){
39553             try{panel.destroy();}catch(e){}
39554         }
39555         this.fireEvent("panelremoved", this, panel);
39556         return panel;
39557     },
39558
39559     /**
39560      * Returns the TabPanel component used by this region
39561      * @return {Roo.TabPanel}
39562      */
39563     getTabs : function(){
39564         return this.tabs;
39565     },
39566
39567     createTool : function(parentEl, className){
39568         var btn = Roo.DomHelper.append(parentEl, {
39569             tag: "div",
39570             cls: "x-layout-tools-button",
39571             children: [ {
39572                 tag: "div",
39573                 cls: "roo-layout-tools-button-inner " + className,
39574                 html: "&#160;"
39575             }]
39576         }, true);
39577         btn.addClassOnOver("roo-layout-tools-button-over");
39578         return btn;
39579     }
39580 });/*
39581  * Based on:
39582  * Ext JS Library 1.1.1
39583  * Copyright(c) 2006-2007, Ext JS, LLC.
39584  *
39585  * Originally Released Under LGPL - original licence link has changed is not relivant.
39586  *
39587  * Fork - LGPL
39588  * <script type="text/javascript">
39589  */
39590  
39591
39592
39593 /**
39594  * @class Roo.SplitLayoutRegion
39595  * @extends Roo.LayoutRegion
39596  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39597  */
39598 Roo.bootstrap.layout.Split = function(config){
39599     this.cursor = config.cursor;
39600     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39601 };
39602
39603 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39604 {
39605     splitTip : "Drag to resize.",
39606     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39607     useSplitTips : false,
39608
39609     applyConfig : function(config){
39610         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39611     },
39612     
39613     onRender : function(ctr,pos) {
39614         
39615         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39616         if(!this.config.split){
39617             return;
39618         }
39619         if(!this.split){
39620             
39621             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39622                             tag: "div",
39623                             id: this.el.id + "-split",
39624                             cls: "roo-layout-split roo-layout-split-"+this.position,
39625                             html: "&#160;"
39626             });
39627             /** The SplitBar for this region 
39628             * @type Roo.SplitBar */
39629             // does not exist yet...
39630             Roo.log([this.position, this.orientation]);
39631             
39632             this.split = new Roo.bootstrap.SplitBar({
39633                 dragElement : splitEl,
39634                 resizingElement: this.el,
39635                 orientation : this.orientation
39636             });
39637             
39638             this.split.on("moved", this.onSplitMove, this);
39639             this.split.useShim = this.config.useShim === true;
39640             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39641             if(this.useSplitTips){
39642                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39643             }
39644             //if(config.collapsible){
39645             //    this.split.el.on("dblclick", this.collapse,  this);
39646             //}
39647         }
39648         if(typeof this.config.minSize != "undefined"){
39649             this.split.minSize = this.config.minSize;
39650         }
39651         if(typeof this.config.maxSize != "undefined"){
39652             this.split.maxSize = this.config.maxSize;
39653         }
39654         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39655             this.hideSplitter();
39656         }
39657         
39658     },
39659
39660     getHMaxSize : function(){
39661          var cmax = this.config.maxSize || 10000;
39662          var center = this.mgr.getRegion("center");
39663          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39664     },
39665
39666     getVMaxSize : function(){
39667          var cmax = this.config.maxSize || 10000;
39668          var center = this.mgr.getRegion("center");
39669          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39670     },
39671
39672     onSplitMove : function(split, newSize){
39673         this.fireEvent("resized", this, newSize);
39674     },
39675     
39676     /** 
39677      * Returns the {@link Roo.SplitBar} for this region.
39678      * @return {Roo.SplitBar}
39679      */
39680     getSplitBar : function(){
39681         return this.split;
39682     },
39683     
39684     hide : function(){
39685         this.hideSplitter();
39686         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39687     },
39688
39689     hideSplitter : function(){
39690         if(this.split){
39691             this.split.el.setLocation(-2000,-2000);
39692             this.split.el.hide();
39693         }
39694     },
39695
39696     show : function(){
39697         if(this.split){
39698             this.split.el.show();
39699         }
39700         Roo.bootstrap.layout.Split.superclass.show.call(this);
39701     },
39702     
39703     beforeSlide: function(){
39704         if(Roo.isGecko){// firefox overflow auto bug workaround
39705             this.bodyEl.clip();
39706             if(this.tabs) {
39707                 this.tabs.bodyEl.clip();
39708             }
39709             if(this.activePanel){
39710                 this.activePanel.getEl().clip();
39711                 
39712                 if(this.activePanel.beforeSlide){
39713                     this.activePanel.beforeSlide();
39714                 }
39715             }
39716         }
39717     },
39718     
39719     afterSlide : function(){
39720         if(Roo.isGecko){// firefox overflow auto bug workaround
39721             this.bodyEl.unclip();
39722             if(this.tabs) {
39723                 this.tabs.bodyEl.unclip();
39724             }
39725             if(this.activePanel){
39726                 this.activePanel.getEl().unclip();
39727                 if(this.activePanel.afterSlide){
39728                     this.activePanel.afterSlide();
39729                 }
39730             }
39731         }
39732     },
39733
39734     initAutoHide : function(){
39735         if(this.autoHide !== false){
39736             if(!this.autoHideHd){
39737                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39738                 this.autoHideHd = {
39739                     "mouseout": function(e){
39740                         if(!e.within(this.el, true)){
39741                             st.delay(500);
39742                         }
39743                     },
39744                     "mouseover" : function(e){
39745                         st.cancel();
39746                     },
39747                     scope : this
39748                 };
39749             }
39750             this.el.on(this.autoHideHd);
39751         }
39752     },
39753
39754     clearAutoHide : function(){
39755         if(this.autoHide !== false){
39756             this.el.un("mouseout", this.autoHideHd.mouseout);
39757             this.el.un("mouseover", this.autoHideHd.mouseover);
39758         }
39759     },
39760
39761     clearMonitor : function(){
39762         Roo.get(document).un("click", this.slideInIf, this);
39763     },
39764
39765     // these names are backwards but not changed for compat
39766     slideOut : function(){
39767         if(this.isSlid || this.el.hasActiveFx()){
39768             return;
39769         }
39770         this.isSlid = true;
39771         if(this.collapseBtn){
39772             this.collapseBtn.hide();
39773         }
39774         this.closeBtnState = this.closeBtn.getStyle('display');
39775         this.closeBtn.hide();
39776         if(this.stickBtn){
39777             this.stickBtn.show();
39778         }
39779         this.el.show();
39780         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39781         this.beforeSlide();
39782         this.el.setStyle("z-index", 10001);
39783         this.el.slideIn(this.getSlideAnchor(), {
39784             callback: function(){
39785                 this.afterSlide();
39786                 this.initAutoHide();
39787                 Roo.get(document).on("click", this.slideInIf, this);
39788                 this.fireEvent("slideshow", this);
39789             },
39790             scope: this,
39791             block: true
39792         });
39793     },
39794
39795     afterSlideIn : function(){
39796         this.clearAutoHide();
39797         this.isSlid = false;
39798         this.clearMonitor();
39799         this.el.setStyle("z-index", "");
39800         if(this.collapseBtn){
39801             this.collapseBtn.show();
39802         }
39803         this.closeBtn.setStyle('display', this.closeBtnState);
39804         if(this.stickBtn){
39805             this.stickBtn.hide();
39806         }
39807         this.fireEvent("slidehide", this);
39808     },
39809
39810     slideIn : function(cb){
39811         if(!this.isSlid || this.el.hasActiveFx()){
39812             Roo.callback(cb);
39813             return;
39814         }
39815         this.isSlid = false;
39816         this.beforeSlide();
39817         this.el.slideOut(this.getSlideAnchor(), {
39818             callback: function(){
39819                 this.el.setLeftTop(-10000, -10000);
39820                 this.afterSlide();
39821                 this.afterSlideIn();
39822                 Roo.callback(cb);
39823             },
39824             scope: this,
39825             block: true
39826         });
39827     },
39828     
39829     slideInIf : function(e){
39830         if(!e.within(this.el)){
39831             this.slideIn();
39832         }
39833     },
39834
39835     animateCollapse : function(){
39836         this.beforeSlide();
39837         this.el.setStyle("z-index", 20000);
39838         var anchor = this.getSlideAnchor();
39839         this.el.slideOut(anchor, {
39840             callback : function(){
39841                 this.el.setStyle("z-index", "");
39842                 this.collapsedEl.slideIn(anchor, {duration:.3});
39843                 this.afterSlide();
39844                 this.el.setLocation(-10000,-10000);
39845                 this.el.hide();
39846                 this.fireEvent("collapsed", this);
39847             },
39848             scope: this,
39849             block: true
39850         });
39851     },
39852
39853     animateExpand : function(){
39854         this.beforeSlide();
39855         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39856         this.el.setStyle("z-index", 20000);
39857         this.collapsedEl.hide({
39858             duration:.1
39859         });
39860         this.el.slideIn(this.getSlideAnchor(), {
39861             callback : function(){
39862                 this.el.setStyle("z-index", "");
39863                 this.afterSlide();
39864                 if(this.split){
39865                     this.split.el.show();
39866                 }
39867                 this.fireEvent("invalidated", this);
39868                 this.fireEvent("expanded", this);
39869             },
39870             scope: this,
39871             block: true
39872         });
39873     },
39874
39875     anchors : {
39876         "west" : "left",
39877         "east" : "right",
39878         "north" : "top",
39879         "south" : "bottom"
39880     },
39881
39882     sanchors : {
39883         "west" : "l",
39884         "east" : "r",
39885         "north" : "t",
39886         "south" : "b"
39887     },
39888
39889     canchors : {
39890         "west" : "tl-tr",
39891         "east" : "tr-tl",
39892         "north" : "tl-bl",
39893         "south" : "bl-tl"
39894     },
39895
39896     getAnchor : function(){
39897         return this.anchors[this.position];
39898     },
39899
39900     getCollapseAnchor : function(){
39901         return this.canchors[this.position];
39902     },
39903
39904     getSlideAnchor : function(){
39905         return this.sanchors[this.position];
39906     },
39907
39908     getAlignAdj : function(){
39909         var cm = this.cmargins;
39910         switch(this.position){
39911             case "west":
39912                 return [0, 0];
39913             break;
39914             case "east":
39915                 return [0, 0];
39916             break;
39917             case "north":
39918                 return [0, 0];
39919             break;
39920             case "south":
39921                 return [0, 0];
39922             break;
39923         }
39924     },
39925
39926     getExpandAdj : function(){
39927         var c = this.collapsedEl, cm = this.cmargins;
39928         switch(this.position){
39929             case "west":
39930                 return [-(cm.right+c.getWidth()+cm.left), 0];
39931             break;
39932             case "east":
39933                 return [cm.right+c.getWidth()+cm.left, 0];
39934             break;
39935             case "north":
39936                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39937             break;
39938             case "south":
39939                 return [0, cm.top+cm.bottom+c.getHeight()];
39940             break;
39941         }
39942     }
39943 });/*
39944  * Based on:
39945  * Ext JS Library 1.1.1
39946  * Copyright(c) 2006-2007, Ext JS, LLC.
39947  *
39948  * Originally Released Under LGPL - original licence link has changed is not relivant.
39949  *
39950  * Fork - LGPL
39951  * <script type="text/javascript">
39952  */
39953 /*
39954  * These classes are private internal classes
39955  */
39956 Roo.bootstrap.layout.Center = function(config){
39957     config.region = "center";
39958     Roo.bootstrap.layout.Region.call(this, config);
39959     this.visible = true;
39960     this.minWidth = config.minWidth || 20;
39961     this.minHeight = config.minHeight || 20;
39962 };
39963
39964 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39965     hide : function(){
39966         // center panel can't be hidden
39967     },
39968     
39969     show : function(){
39970         // center panel can't be hidden
39971     },
39972     
39973     getMinWidth: function(){
39974         return this.minWidth;
39975     },
39976     
39977     getMinHeight: function(){
39978         return this.minHeight;
39979     }
39980 });
39981
39982
39983
39984
39985  
39986
39987
39988
39989
39990
39991
39992 Roo.bootstrap.layout.North = function(config)
39993 {
39994     config.region = 'north';
39995     config.cursor = 'n-resize';
39996     
39997     Roo.bootstrap.layout.Split.call(this, config);
39998     
39999     
40000     if(this.split){
40001         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40002         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40003         this.split.el.addClass("roo-layout-split-v");
40004     }
40005     //var size = config.initialSize || config.height;
40006     //if(this.el && typeof size != "undefined"){
40007     //    this.el.setHeight(size);
40008     //}
40009 };
40010 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40011 {
40012     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40013      
40014      
40015     onRender : function(ctr, pos)
40016     {
40017         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40018         var size = this.config.initialSize || this.config.height;
40019         if(this.el && typeof size != "undefined"){
40020             this.el.setHeight(size);
40021         }
40022     
40023     },
40024     
40025     getBox : function(){
40026         if(this.collapsed){
40027             return this.collapsedEl.getBox();
40028         }
40029         var box = this.el.getBox();
40030         if(this.split){
40031             box.height += this.split.el.getHeight();
40032         }
40033         return box;
40034     },
40035     
40036     updateBox : function(box){
40037         if(this.split && !this.collapsed){
40038             box.height -= this.split.el.getHeight();
40039             this.split.el.setLeft(box.x);
40040             this.split.el.setTop(box.y+box.height);
40041             this.split.el.setWidth(box.width);
40042         }
40043         if(this.collapsed){
40044             this.updateBody(box.width, null);
40045         }
40046         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40047     }
40048 });
40049
40050
40051
40052
40053
40054 Roo.bootstrap.layout.South = function(config){
40055     config.region = 'south';
40056     config.cursor = 's-resize';
40057     Roo.bootstrap.layout.Split.call(this, config);
40058     if(this.split){
40059         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40060         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40061         this.split.el.addClass("roo-layout-split-v");
40062     }
40063     
40064 };
40065
40066 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40067     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40068     
40069     onRender : function(ctr, pos)
40070     {
40071         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40072         var size = this.config.initialSize || this.config.height;
40073         if(this.el && typeof size != "undefined"){
40074             this.el.setHeight(size);
40075         }
40076     
40077     },
40078     
40079     getBox : function(){
40080         if(this.collapsed){
40081             return this.collapsedEl.getBox();
40082         }
40083         var box = this.el.getBox();
40084         if(this.split){
40085             var sh = this.split.el.getHeight();
40086             box.height += sh;
40087             box.y -= sh;
40088         }
40089         return box;
40090     },
40091     
40092     updateBox : function(box){
40093         if(this.split && !this.collapsed){
40094             var sh = this.split.el.getHeight();
40095             box.height -= sh;
40096             box.y += sh;
40097             this.split.el.setLeft(box.x);
40098             this.split.el.setTop(box.y-sh);
40099             this.split.el.setWidth(box.width);
40100         }
40101         if(this.collapsed){
40102             this.updateBody(box.width, null);
40103         }
40104         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40105     }
40106 });
40107
40108 Roo.bootstrap.layout.East = function(config){
40109     config.region = "east";
40110     config.cursor = "e-resize";
40111     Roo.bootstrap.layout.Split.call(this, config);
40112     if(this.split){
40113         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40114         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40115         this.split.el.addClass("roo-layout-split-h");
40116     }
40117     
40118 };
40119 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40120     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40121     
40122     onRender : function(ctr, pos)
40123     {
40124         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40125         var size = this.config.initialSize || this.config.width;
40126         if(this.el && typeof size != "undefined"){
40127             this.el.setWidth(size);
40128         }
40129     
40130     },
40131     
40132     getBox : function(){
40133         if(this.collapsed){
40134             return this.collapsedEl.getBox();
40135         }
40136         var box = this.el.getBox();
40137         if(this.split){
40138             var sw = this.split.el.getWidth();
40139             box.width += sw;
40140             box.x -= sw;
40141         }
40142         return box;
40143     },
40144
40145     updateBox : function(box){
40146         if(this.split && !this.collapsed){
40147             var sw = this.split.el.getWidth();
40148             box.width -= sw;
40149             this.split.el.setLeft(box.x);
40150             this.split.el.setTop(box.y);
40151             this.split.el.setHeight(box.height);
40152             box.x += sw;
40153         }
40154         if(this.collapsed){
40155             this.updateBody(null, box.height);
40156         }
40157         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40158     }
40159 });
40160
40161 Roo.bootstrap.layout.West = function(config){
40162     config.region = "west";
40163     config.cursor = "w-resize";
40164     
40165     Roo.bootstrap.layout.Split.call(this, config);
40166     if(this.split){
40167         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40168         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40169         this.split.el.addClass("roo-layout-split-h");
40170     }
40171     
40172 };
40173 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40174     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40175     
40176     onRender: function(ctr, pos)
40177     {
40178         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40179         var size = this.config.initialSize || this.config.width;
40180         if(typeof size != "undefined"){
40181             this.el.setWidth(size);
40182         }
40183     },
40184     
40185     getBox : function(){
40186         if(this.collapsed){
40187             return this.collapsedEl.getBox();
40188         }
40189         var box = this.el.getBox();
40190         if (box.width == 0) {
40191             box.width = this.config.width; // kludge?
40192         }
40193         if(this.split){
40194             box.width += this.split.el.getWidth();
40195         }
40196         return box;
40197     },
40198     
40199     updateBox : function(box){
40200         if(this.split && !this.collapsed){
40201             var sw = this.split.el.getWidth();
40202             box.width -= sw;
40203             this.split.el.setLeft(box.x+box.width);
40204             this.split.el.setTop(box.y);
40205             this.split.el.setHeight(box.height);
40206         }
40207         if(this.collapsed){
40208             this.updateBody(null, box.height);
40209         }
40210         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40211     }
40212 });/*
40213  * Based on:
40214  * Ext JS Library 1.1.1
40215  * Copyright(c) 2006-2007, Ext JS, LLC.
40216  *
40217  * Originally Released Under LGPL - original licence link has changed is not relivant.
40218  *
40219  * Fork - LGPL
40220  * <script type="text/javascript">
40221  */
40222 /**
40223  * @class Roo.bootstrap.paenl.Content
40224  * @extends Roo.util.Observable
40225  * @builder-top
40226  * @children Roo.bootstrap.Component
40227  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40228  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40229  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40230  * @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
40231  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40232  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40233  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40234  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40235  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40236  * @cfg {String} title          The title for this panel
40237  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40238  * @cfg {String} url            Calls {@link #setUrl} with this value
40239  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40240  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40241  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40242  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40243  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40244  * @cfg {Boolean} badges render the badges
40245  * @cfg {String} cls  extra classes to use  
40246  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40247  
40248  * @constructor
40249  * Create a new ContentPanel.
40250  * @param {String/Object} config A string to set only the title or a config object
40251  
40252  */
40253 Roo.bootstrap.panel.Content = function( config){
40254     
40255     this.tpl = config.tpl || false;
40256     
40257     var el = config.el;
40258     var content = config.content;
40259
40260     if(config.autoCreate){ // xtype is available if this is called from factory
40261         el = Roo.id();
40262     }
40263     this.el = Roo.get(el);
40264     if(!this.el && config && config.autoCreate){
40265         if(typeof config.autoCreate == "object"){
40266             if(!config.autoCreate.id){
40267                 config.autoCreate.id = config.id||el;
40268             }
40269             this.el = Roo.DomHelper.append(document.body,
40270                         config.autoCreate, true);
40271         }else{
40272             var elcfg =  {
40273                 tag: "div",
40274                 cls: (config.cls || '') +
40275                     (config.background ? ' bg-' + config.background : '') +
40276                     " roo-layout-inactive-content",
40277                 id: config.id||el
40278             };
40279             if (config.iframe) {
40280                 elcfg.cn = [
40281                     {
40282                         tag : 'iframe',
40283                         style : 'border: 0px',
40284                         src : 'about:blank'
40285                     }
40286                 ];
40287             }
40288               
40289             if (config.html) {
40290                 elcfg.html = config.html;
40291                 
40292             }
40293                         
40294             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40295             if (config.iframe) {
40296                 this.iframeEl = this.el.select('iframe',true).first();
40297             }
40298             
40299         }
40300     } 
40301     this.closable = false;
40302     this.loaded = false;
40303     this.active = false;
40304    
40305       
40306     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40307         
40308         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40309         
40310         this.wrapEl = this.el; //this.el.wrap();
40311         var ti = [];
40312         if (config.toolbar.items) {
40313             ti = config.toolbar.items ;
40314             delete config.toolbar.items ;
40315         }
40316         
40317         var nitems = [];
40318         this.toolbar.render(this.wrapEl, 'before');
40319         for(var i =0;i < ti.length;i++) {
40320           //  Roo.log(['add child', items[i]]);
40321             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40322         }
40323         this.toolbar.items = nitems;
40324         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40325         delete config.toolbar;
40326         
40327     }
40328     /*
40329     // xtype created footer. - not sure if will work as we normally have to render first..
40330     if (this.footer && !this.footer.el && this.footer.xtype) {
40331         if (!this.wrapEl) {
40332             this.wrapEl = this.el.wrap();
40333         }
40334     
40335         this.footer.container = this.wrapEl.createChild();
40336          
40337         this.footer = Roo.factory(this.footer, Roo);
40338         
40339     }
40340     */
40341     
40342      if(typeof config == "string"){
40343         this.title = config;
40344     }else{
40345         Roo.apply(this, config);
40346     }
40347     
40348     if(this.resizeEl){
40349         this.resizeEl = Roo.get(this.resizeEl, true);
40350     }else{
40351         this.resizeEl = this.el;
40352     }
40353     // handle view.xtype
40354     
40355  
40356     
40357     
40358     this.addEvents({
40359         /**
40360          * @event activate
40361          * Fires when this panel is activated. 
40362          * @param {Roo.ContentPanel} this
40363          */
40364         "activate" : true,
40365         /**
40366          * @event deactivate
40367          * Fires when this panel is activated. 
40368          * @param {Roo.ContentPanel} this
40369          */
40370         "deactivate" : true,
40371
40372         /**
40373          * @event resize
40374          * Fires when this panel is resized if fitToFrame is true.
40375          * @param {Roo.ContentPanel} this
40376          * @param {Number} width The width after any component adjustments
40377          * @param {Number} height The height after any component adjustments
40378          */
40379         "resize" : true,
40380         
40381          /**
40382          * @event render
40383          * Fires when this tab is created
40384          * @param {Roo.ContentPanel} this
40385          */
40386         "render" : true,
40387         
40388           /**
40389          * @event scroll
40390          * Fires when this content is scrolled
40391          * @param {Roo.ContentPanel} this
40392          * @param {Event} scrollEvent
40393          */
40394         "scroll" : true
40395         
40396         
40397         
40398     });
40399     
40400
40401     
40402     
40403     if(this.autoScroll && !this.iframe){
40404         this.resizeEl.setStyle("overflow", "auto");
40405         this.resizeEl.on('scroll', this.onScroll, this);
40406     } else {
40407         // fix randome scrolling
40408         //this.el.on('scroll', function() {
40409         //    Roo.log('fix random scolling');
40410         //    this.scrollTo('top',0); 
40411         //});
40412     }
40413     content = content || this.content;
40414     if(content){
40415         this.setContent(content);
40416     }
40417     if(config && config.url){
40418         this.setUrl(this.url, this.params, this.loadOnce);
40419     }
40420     
40421     
40422     
40423     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40424     
40425     if (this.view && typeof(this.view.xtype) != 'undefined') {
40426         this.view.el = this.el.appendChild(document.createElement("div"));
40427         this.view = Roo.factory(this.view); 
40428         this.view.render  &&  this.view.render(false, '');  
40429     }
40430     
40431     
40432     this.fireEvent('render', this);
40433 };
40434
40435 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40436     
40437     cls : '',
40438     background : '',
40439     
40440     tabTip : '',
40441     
40442     iframe : false,
40443     iframeEl : false,
40444     
40445     /* Resize Element - use this to work out scroll etc. */
40446     resizeEl : false,
40447     
40448     setRegion : function(region){
40449         this.region = region;
40450         this.setActiveClass(region && !this.background);
40451     },
40452     
40453     
40454     setActiveClass: function(state)
40455     {
40456         if(state){
40457            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40458            this.el.setStyle('position','relative');
40459         }else{
40460            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40461            this.el.setStyle('position', 'absolute');
40462         } 
40463     },
40464     
40465     /**
40466      * Returns the toolbar for this Panel if one was configured. 
40467      * @return {Roo.Toolbar} 
40468      */
40469     getToolbar : function(){
40470         return this.toolbar;
40471     },
40472     
40473     setActiveState : function(active)
40474     {
40475         this.active = active;
40476         this.setActiveClass(active);
40477         if(!active){
40478             if(this.fireEvent("deactivate", this) === false){
40479                 return false;
40480             }
40481             return true;
40482         }
40483         this.fireEvent("activate", this);
40484         return true;
40485     },
40486     /**
40487      * Updates this panel's element (not for iframe)
40488      * @param {String} content The new content
40489      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40490     */
40491     setContent : function(content, loadScripts){
40492         if (this.iframe) {
40493             return;
40494         }
40495         
40496         this.el.update(content, loadScripts);
40497     },
40498
40499     ignoreResize : function(w, h){
40500         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40501             return true;
40502         }else{
40503             this.lastSize = {width: w, height: h};
40504             return false;
40505         }
40506     },
40507     /**
40508      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40509      * @return {Roo.UpdateManager} The UpdateManager
40510      */
40511     getUpdateManager : function(){
40512         if (this.iframe) {
40513             return false;
40514         }
40515         return this.el.getUpdateManager();
40516     },
40517      /**
40518      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40519      * Does not work with IFRAME contents
40520      * @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:
40521 <pre><code>
40522 panel.load({
40523     url: "your-url.php",
40524     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40525     callback: yourFunction,
40526     scope: yourObject, //(optional scope)
40527     discardUrl: false,
40528     nocache: false,
40529     text: "Loading...",
40530     timeout: 30,
40531     scripts: false
40532 });
40533 </code></pre>
40534      
40535      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40536      * 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.
40537      * @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}
40538      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40539      * @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.
40540      * @return {Roo.ContentPanel} this
40541      */
40542     load : function(){
40543         
40544         if (this.iframe) {
40545             return this;
40546         }
40547         
40548         var um = this.el.getUpdateManager();
40549         um.update.apply(um, arguments);
40550         return this;
40551     },
40552
40553
40554     /**
40555      * 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.
40556      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40557      * @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)
40558      * @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)
40559      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40560      */
40561     setUrl : function(url, params, loadOnce){
40562         if (this.iframe) {
40563             this.iframeEl.dom.src = url;
40564             return false;
40565         }
40566         
40567         if(this.refreshDelegate){
40568             this.removeListener("activate", this.refreshDelegate);
40569         }
40570         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40571         this.on("activate", this.refreshDelegate);
40572         return this.el.getUpdateManager();
40573     },
40574     
40575     _handleRefresh : function(url, params, loadOnce){
40576         if(!loadOnce || !this.loaded){
40577             var updater = this.el.getUpdateManager();
40578             updater.update(url, params, this._setLoaded.createDelegate(this));
40579         }
40580     },
40581     
40582     _setLoaded : function(){
40583         this.loaded = true;
40584     }, 
40585     
40586     /**
40587      * Returns this panel's id
40588      * @return {String} 
40589      */
40590     getId : function(){
40591         return this.el.id;
40592     },
40593     
40594     /** 
40595      * Returns this panel's element - used by regiosn to add.
40596      * @return {Roo.Element} 
40597      */
40598     getEl : function(){
40599         return this.wrapEl || this.el;
40600     },
40601     
40602    
40603     
40604     adjustForComponents : function(width, height)
40605     {
40606         //Roo.log('adjustForComponents ');
40607         if(this.resizeEl != this.el){
40608             width -= this.el.getFrameWidth('lr');
40609             height -= this.el.getFrameWidth('tb');
40610         }
40611         if(this.toolbar){
40612             var te = this.toolbar.getEl();
40613             te.setWidth(width);
40614             height -= te.getHeight();
40615         }
40616         if(this.footer){
40617             var te = this.footer.getEl();
40618             te.setWidth(width);
40619             height -= te.getHeight();
40620         }
40621         
40622         
40623         if(this.adjustments){
40624             width += this.adjustments[0];
40625             height += this.adjustments[1];
40626         }
40627         return {"width": width, "height": height};
40628     },
40629     
40630     setSize : function(width, height){
40631         if(this.fitToFrame && !this.ignoreResize(width, height)){
40632             if(this.fitContainer && this.resizeEl != this.el){
40633                 this.el.setSize(width, height);
40634             }
40635             var size = this.adjustForComponents(width, height);
40636             if (this.iframe) {
40637                 this.iframeEl.setSize(width,height);
40638             }
40639             
40640             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40641             this.fireEvent('resize', this, size.width, size.height);
40642             
40643             
40644         }
40645     },
40646     
40647     /**
40648      * Returns this panel's title
40649      * @return {String} 
40650      */
40651     getTitle : function(){
40652         
40653         if (typeof(this.title) != 'object') {
40654             return this.title;
40655         }
40656         
40657         var t = '';
40658         for (var k in this.title) {
40659             if (!this.title.hasOwnProperty(k)) {
40660                 continue;
40661             }
40662             
40663             if (k.indexOf('-') >= 0) {
40664                 var s = k.split('-');
40665                 for (var i = 0; i<s.length; i++) {
40666                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40667                 }
40668             } else {
40669                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40670             }
40671         }
40672         return t;
40673     },
40674     
40675     /**
40676      * Set this panel's title
40677      * @param {String} title
40678      */
40679     setTitle : function(title){
40680         this.title = title;
40681         if(this.region){
40682             this.region.updatePanelTitle(this, title);
40683         }
40684     },
40685     
40686     /**
40687      * Returns true is this panel was configured to be closable
40688      * @return {Boolean} 
40689      */
40690     isClosable : function(){
40691         return this.closable;
40692     },
40693     
40694     beforeSlide : function(){
40695         this.el.clip();
40696         this.resizeEl.clip();
40697     },
40698     
40699     afterSlide : function(){
40700         this.el.unclip();
40701         this.resizeEl.unclip();
40702     },
40703     
40704     /**
40705      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40706      *   Will fail silently if the {@link #setUrl} method has not been called.
40707      *   This does not activate the panel, just updates its content.
40708      */
40709     refresh : function(){
40710         if(this.refreshDelegate){
40711            this.loaded = false;
40712            this.refreshDelegate();
40713         }
40714     },
40715     
40716     /**
40717      * Destroys this panel
40718      */
40719     destroy : function(){
40720         this.el.removeAllListeners();
40721         var tempEl = document.createElement("span");
40722         tempEl.appendChild(this.el.dom);
40723         tempEl.innerHTML = "";
40724         this.el.remove();
40725         this.el = null;
40726     },
40727     
40728     /**
40729      * form - if the content panel contains a form - this is a reference to it.
40730      * @type {Roo.form.Form}
40731      */
40732     form : false,
40733     /**
40734      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40735      *    This contains a reference to it.
40736      * @type {Roo.View}
40737      */
40738     view : false,
40739     
40740       /**
40741      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40742      * <pre><code>
40743
40744 layout.addxtype({
40745        xtype : 'Form',
40746        items: [ .... ]
40747    }
40748 );
40749
40750 </code></pre>
40751      * @param {Object} cfg Xtype definition of item to add.
40752      */
40753     
40754     
40755     getChildContainer: function () {
40756         return this.getEl();
40757     },
40758     
40759     
40760     onScroll : function(e)
40761     {
40762         this.fireEvent('scroll', this, e);
40763     }
40764     
40765     
40766     /*
40767         var  ret = new Roo.factory(cfg);
40768         return ret;
40769         
40770         
40771         // add form..
40772         if (cfg.xtype.match(/^Form$/)) {
40773             
40774             var el;
40775             //if (this.footer) {
40776             //    el = this.footer.container.insertSibling(false, 'before');
40777             //} else {
40778                 el = this.el.createChild();
40779             //}
40780
40781             this.form = new  Roo.form.Form(cfg);
40782             
40783             
40784             if ( this.form.allItems.length) {
40785                 this.form.render(el.dom);
40786             }
40787             return this.form;
40788         }
40789         // should only have one of theses..
40790         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40791             // views.. should not be just added - used named prop 'view''
40792             
40793             cfg.el = this.el.appendChild(document.createElement("div"));
40794             // factory?
40795             
40796             var ret = new Roo.factory(cfg);
40797              
40798              ret.render && ret.render(false, ''); // render blank..
40799             this.view = ret;
40800             return ret;
40801         }
40802         return false;
40803     }
40804     \*/
40805 });
40806  
40807 /**
40808  * @class Roo.bootstrap.panel.Grid
40809  * @extends Roo.bootstrap.panel.Content
40810  * @constructor
40811  * Create a new GridPanel.
40812  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40813  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40814  * @param {Object} config A the config object
40815   
40816  */
40817
40818
40819
40820 Roo.bootstrap.panel.Grid = function(config)
40821 {
40822     
40823       
40824     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40825         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40826
40827     config.el = this.wrapper;
40828     //this.el = this.wrapper;
40829     
40830       if (config.container) {
40831         // ctor'ed from a Border/panel.grid
40832         
40833         
40834         this.wrapper.setStyle("overflow", "hidden");
40835         this.wrapper.addClass('roo-grid-container');
40836
40837     }
40838     
40839     
40840     if(config.toolbar){
40841         var tool_el = this.wrapper.createChild();    
40842         this.toolbar = Roo.factory(config.toolbar);
40843         var ti = [];
40844         if (config.toolbar.items) {
40845             ti = config.toolbar.items ;
40846             delete config.toolbar.items ;
40847         }
40848         
40849         var nitems = [];
40850         this.toolbar.render(tool_el);
40851         for(var i =0;i < ti.length;i++) {
40852           //  Roo.log(['add child', items[i]]);
40853             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40854         }
40855         this.toolbar.items = nitems;
40856         
40857         delete config.toolbar;
40858     }
40859     
40860     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40861     config.grid.scrollBody = true;;
40862     config.grid.monitorWindowResize = false; // turn off autosizing
40863     config.grid.autoHeight = false;
40864     config.grid.autoWidth = false;
40865     
40866     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40867     
40868     if (config.background) {
40869         // render grid on panel activation (if panel background)
40870         this.on('activate', function(gp) {
40871             if (!gp.grid.rendered) {
40872                 gp.grid.render(this.wrapper);
40873                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40874             }
40875         });
40876             
40877     } else {
40878         this.grid.render(this.wrapper);
40879         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40880
40881     }
40882     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40883     // ??? needed ??? config.el = this.wrapper;
40884     
40885     
40886     
40887   
40888     // xtype created footer. - not sure if will work as we normally have to render first..
40889     if (this.footer && !this.footer.el && this.footer.xtype) {
40890         
40891         var ctr = this.grid.getView().getFooterPanel(true);
40892         this.footer.dataSource = this.grid.dataSource;
40893         this.footer = Roo.factory(this.footer, Roo);
40894         this.footer.render(ctr);
40895         
40896     }
40897     
40898     
40899     
40900     
40901      
40902 };
40903
40904 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40905     getId : function(){
40906         return this.grid.id;
40907     },
40908     
40909     /**
40910      * Returns the grid for this panel
40911      * @return {Roo.bootstrap.Table} 
40912      */
40913     getGrid : function(){
40914         return this.grid;    
40915     },
40916     
40917     setSize : function(width, height){
40918         if(!this.ignoreResize(width, height)){
40919             var grid = this.grid;
40920             var size = this.adjustForComponents(width, height);
40921             // tfoot is not a footer?
40922           
40923             
40924             var gridel = grid.getGridEl();
40925             gridel.setSize(size.width, size.height);
40926             
40927             var tbd = grid.getGridEl().select('tbody', true).first();
40928             var thd = grid.getGridEl().select('thead',true).first();
40929             var tbf= grid.getGridEl().select('tfoot', true).first();
40930
40931             if (tbf) {
40932                 size.height -= tbf.getHeight();
40933             }
40934             if (thd) {
40935                 size.height -= thd.getHeight();
40936             }
40937             
40938             tbd.setSize(size.width, size.height );
40939             // this is for the account management tab -seems to work there.
40940             var thd = grid.getGridEl().select('thead',true).first();
40941             //if (tbd) {
40942             //    tbd.setSize(size.width, size.height - thd.getHeight());
40943             //}
40944              
40945             grid.autoSize();
40946         }
40947     },
40948      
40949     
40950     
40951     beforeSlide : function(){
40952         this.grid.getView().scroller.clip();
40953     },
40954     
40955     afterSlide : function(){
40956         this.grid.getView().scroller.unclip();
40957     },
40958     
40959     destroy : function(){
40960         this.grid.destroy();
40961         delete this.grid;
40962         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40963     }
40964 });
40965
40966 /**
40967  * @class Roo.bootstrap.panel.Nest
40968  * @extends Roo.bootstrap.panel.Content
40969  * @constructor
40970  * Create a new Panel, that can contain a layout.Border.
40971  * 
40972  * 
40973  * @param {String/Object} config A string to set only the title or a config object
40974  */
40975 Roo.bootstrap.panel.Nest = function(config)
40976 {
40977     // construct with only one argument..
40978     /* FIXME - implement nicer consturctors
40979     if (layout.layout) {
40980         config = layout;
40981         layout = config.layout;
40982         delete config.layout;
40983     }
40984     if (layout.xtype && !layout.getEl) {
40985         // then layout needs constructing..
40986         layout = Roo.factory(layout, Roo);
40987     }
40988     */
40989     
40990     config.el =  config.layout.getEl();
40991     
40992     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40993     
40994     config.layout.monitorWindowResize = false; // turn off autosizing
40995     this.layout = config.layout;
40996     this.layout.getEl().addClass("roo-layout-nested-layout");
40997     this.layout.parent = this;
40998     
40999     
41000     
41001     
41002 };
41003
41004 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41005     /**
41006     * @cfg {Roo.BorderLayout} layout The layout for this panel
41007     */
41008     layout : false,
41009
41010     setSize : function(width, height){
41011         if(!this.ignoreResize(width, height)){
41012             var size = this.adjustForComponents(width, height);
41013             var el = this.layout.getEl();
41014             if (size.height < 1) {
41015                 el.setWidth(size.width);   
41016             } else {
41017                 el.setSize(size.width, size.height);
41018             }
41019             var touch = el.dom.offsetWidth;
41020             this.layout.layout();
41021             // ie requires a double layout on the first pass
41022             if(Roo.isIE && !this.initialized){
41023                 this.initialized = true;
41024                 this.layout.layout();
41025             }
41026         }
41027     },
41028     
41029     // activate all subpanels if not currently active..
41030     
41031     setActiveState : function(active){
41032         this.active = active;
41033         this.setActiveClass(active);
41034         
41035         if(!active){
41036             this.fireEvent("deactivate", this);
41037             return;
41038         }
41039         
41040         this.fireEvent("activate", this);
41041         // not sure if this should happen before or after..
41042         if (!this.layout) {
41043             return; // should not happen..
41044         }
41045         var reg = false;
41046         for (var r in this.layout.regions) {
41047             reg = this.layout.getRegion(r);
41048             if (reg.getActivePanel()) {
41049                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41050                 reg.setActivePanel(reg.getActivePanel());
41051                 continue;
41052             }
41053             if (!reg.panels.length) {
41054                 continue;
41055             }
41056             reg.showPanel(reg.getPanel(0));
41057         }
41058         
41059         
41060         
41061         
41062     },
41063     
41064     /**
41065      * Returns the nested BorderLayout for this panel
41066      * @return {Roo.BorderLayout} 
41067      */
41068     getLayout : function(){
41069         return this.layout;
41070     },
41071     
41072      /**
41073      * Adds a xtype elements to the layout of the nested panel
41074      * <pre><code>
41075
41076 panel.addxtype({
41077        xtype : 'ContentPanel',
41078        region: 'west',
41079        items: [ .... ]
41080    }
41081 );
41082
41083 panel.addxtype({
41084         xtype : 'NestedLayoutPanel',
41085         region: 'west',
41086         layout: {
41087            center: { },
41088            west: { }   
41089         },
41090         items : [ ... list of content panels or nested layout panels.. ]
41091    }
41092 );
41093 </code></pre>
41094      * @param {Object} cfg Xtype definition of item to add.
41095      */
41096     addxtype : function(cfg) {
41097         return this.layout.addxtype(cfg);
41098     
41099     }
41100 });/*
41101  * Based on:
41102  * Ext JS Library 1.1.1
41103  * Copyright(c) 2006-2007, Ext JS, LLC.
41104  *
41105  * Originally Released Under LGPL - original licence link has changed is not relivant.
41106  *
41107  * Fork - LGPL
41108  * <script type="text/javascript">
41109  */
41110 /**
41111  * @class Roo.TabPanel
41112  * @extends Roo.util.Observable
41113  * A lightweight tab container.
41114  * <br><br>
41115  * Usage:
41116  * <pre><code>
41117 // basic tabs 1, built from existing content
41118 var tabs = new Roo.TabPanel("tabs1");
41119 tabs.addTab("script", "View Script");
41120 tabs.addTab("markup", "View Markup");
41121 tabs.activate("script");
41122
41123 // more advanced tabs, built from javascript
41124 var jtabs = new Roo.TabPanel("jtabs");
41125 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41126
41127 // set up the UpdateManager
41128 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41129 var updater = tab2.getUpdateManager();
41130 updater.setDefaultUrl("ajax1.htm");
41131 tab2.on('activate', updater.refresh, updater, true);
41132
41133 // Use setUrl for Ajax loading
41134 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41135 tab3.setUrl("ajax2.htm", null, true);
41136
41137 // Disabled tab
41138 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41139 tab4.disable();
41140
41141 jtabs.activate("jtabs-1");
41142  * </code></pre>
41143  * @constructor
41144  * Create a new TabPanel.
41145  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41146  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41147  */
41148 Roo.bootstrap.panel.Tabs = function(config){
41149     /**
41150     * The container element for this TabPanel.
41151     * @type Roo.Element
41152     */
41153     this.el = Roo.get(config.el);
41154     delete config.el;
41155     if(config){
41156         if(typeof config == "boolean"){
41157             this.tabPosition = config ? "bottom" : "top";
41158         }else{
41159             Roo.apply(this, config);
41160         }
41161     }
41162     
41163     if(this.tabPosition == "bottom"){
41164         // if tabs are at the bottom = create the body first.
41165         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41166         this.el.addClass("roo-tabs-bottom");
41167     }
41168     // next create the tabs holders
41169     
41170     if (this.tabPosition == "west"){
41171         
41172         var reg = this.region; // fake it..
41173         while (reg) {
41174             if (!reg.mgr.parent) {
41175                 break;
41176             }
41177             reg = reg.mgr.parent.region;
41178         }
41179         Roo.log("got nest?");
41180         Roo.log(reg);
41181         if (reg.mgr.getRegion('west')) {
41182             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41183             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41184             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41185             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41186             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41187         
41188             
41189         }
41190         
41191         
41192     } else {
41193      
41194         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41195         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41196         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41197         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41198     }
41199     
41200     
41201     if(Roo.isIE){
41202         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41203     }
41204     
41205     // finally - if tabs are at the top, then create the body last..
41206     if(this.tabPosition != "bottom"){
41207         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41208          * @type Roo.Element
41209          */
41210         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41211         this.el.addClass("roo-tabs-top");
41212     }
41213     this.items = [];
41214
41215     this.bodyEl.setStyle("position", "relative");
41216
41217     this.active = null;
41218     this.activateDelegate = this.activate.createDelegate(this);
41219
41220     this.addEvents({
41221         /**
41222          * @event tabchange
41223          * Fires when the active tab changes
41224          * @param {Roo.TabPanel} this
41225          * @param {Roo.TabPanelItem} activePanel The new active tab
41226          */
41227         "tabchange": true,
41228         /**
41229          * @event beforetabchange
41230          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41231          * @param {Roo.TabPanel} this
41232          * @param {Object} e Set cancel to true on this object to cancel the tab change
41233          * @param {Roo.TabPanelItem} tab The tab being changed to
41234          */
41235         "beforetabchange" : true
41236     });
41237
41238     Roo.EventManager.onWindowResize(this.onResize, this);
41239     this.cpad = this.el.getPadding("lr");
41240     this.hiddenCount = 0;
41241
41242
41243     // toolbar on the tabbar support...
41244     if (this.toolbar) {
41245         alert("no toolbar support yet");
41246         this.toolbar  = false;
41247         /*
41248         var tcfg = this.toolbar;
41249         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41250         this.toolbar = new Roo.Toolbar(tcfg);
41251         if (Roo.isSafari) {
41252             var tbl = tcfg.container.child('table', true);
41253             tbl.setAttribute('width', '100%');
41254         }
41255         */
41256         
41257     }
41258    
41259
41260
41261     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41262 };
41263
41264 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41265     /*
41266      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41267      */
41268     tabPosition : "top",
41269     /*
41270      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41271      */
41272     currentTabWidth : 0,
41273     /*
41274      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41275      */
41276     minTabWidth : 40,
41277     /*
41278      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41279      */
41280     maxTabWidth : 250,
41281     /*
41282      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41283      */
41284     preferredTabWidth : 175,
41285     /*
41286      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41287      */
41288     resizeTabs : false,
41289     /*
41290      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41291      */
41292     monitorResize : true,
41293     /*
41294      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41295      */
41296     toolbar : false,  // set by caller..
41297     
41298     region : false, /// set by caller
41299     
41300     disableTooltips : true, // not used yet...
41301
41302     /**
41303      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41304      * @param {String} id The id of the div to use <b>or create</b>
41305      * @param {String} text The text for the tab
41306      * @param {String} content (optional) Content to put in the TabPanelItem body
41307      * @param {Boolean} closable (optional) True to create a close icon on the tab
41308      * @return {Roo.TabPanelItem} The created TabPanelItem
41309      */
41310     addTab : function(id, text, content, closable, tpl)
41311     {
41312         var item = new Roo.bootstrap.panel.TabItem({
41313             panel: this,
41314             id : id,
41315             text : text,
41316             closable : closable,
41317             tpl : tpl
41318         });
41319         this.addTabItem(item);
41320         if(content){
41321             item.setContent(content);
41322         }
41323         return item;
41324     },
41325
41326     /**
41327      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41328      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41329      * @return {Roo.TabPanelItem}
41330      */
41331     getTab : function(id){
41332         return this.items[id];
41333     },
41334
41335     /**
41336      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41337      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41338      */
41339     hideTab : function(id){
41340         var t = this.items[id];
41341         if(!t.isHidden()){
41342            t.setHidden(true);
41343            this.hiddenCount++;
41344            this.autoSizeTabs();
41345         }
41346     },
41347
41348     /**
41349      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41350      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41351      */
41352     unhideTab : function(id){
41353         var t = this.items[id];
41354         if(t.isHidden()){
41355            t.setHidden(false);
41356            this.hiddenCount--;
41357            this.autoSizeTabs();
41358         }
41359     },
41360
41361     /**
41362      * Adds an existing {@link Roo.TabPanelItem}.
41363      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41364      */
41365     addTabItem : function(item)
41366     {
41367         this.items[item.id] = item;
41368         this.items.push(item);
41369         this.autoSizeTabs();
41370       //  if(this.resizeTabs){
41371     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41372   //         this.autoSizeTabs();
41373 //        }else{
41374 //            item.autoSize();
41375        // }
41376     },
41377
41378     /**
41379      * Removes a {@link Roo.TabPanelItem}.
41380      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41381      */
41382     removeTab : function(id){
41383         var items = this.items;
41384         var tab = items[id];
41385         if(!tab) { return; }
41386         var index = items.indexOf(tab);
41387         if(this.active == tab && items.length > 1){
41388             var newTab = this.getNextAvailable(index);
41389             if(newTab) {
41390                 newTab.activate();
41391             }
41392         }
41393         this.stripEl.dom.removeChild(tab.pnode.dom);
41394         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41395             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41396         }
41397         items.splice(index, 1);
41398         delete this.items[tab.id];
41399         tab.fireEvent("close", tab);
41400         tab.purgeListeners();
41401         this.autoSizeTabs();
41402     },
41403
41404     getNextAvailable : function(start){
41405         var items = this.items;
41406         var index = start;
41407         // look for a next tab that will slide over to
41408         // replace the one being removed
41409         while(index < items.length){
41410             var item = items[++index];
41411             if(item && !item.isHidden()){
41412                 return item;
41413             }
41414         }
41415         // if one isn't found select the previous tab (on the left)
41416         index = start;
41417         while(index >= 0){
41418             var item = items[--index];
41419             if(item && !item.isHidden()){
41420                 return item;
41421             }
41422         }
41423         return null;
41424     },
41425
41426     /**
41427      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41428      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41429      */
41430     disableTab : function(id){
41431         var tab = this.items[id];
41432         if(tab && this.active != tab){
41433             tab.disable();
41434         }
41435     },
41436
41437     /**
41438      * Enables a {@link Roo.TabPanelItem} that is disabled.
41439      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41440      */
41441     enableTab : function(id){
41442         var tab = this.items[id];
41443         tab.enable();
41444     },
41445
41446     /**
41447      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41448      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41449      * @return {Roo.TabPanelItem} The TabPanelItem.
41450      */
41451     activate : function(id)
41452     {
41453         //Roo.log('activite:'  + id);
41454         
41455         var tab = this.items[id];
41456         if(!tab){
41457             return null;
41458         }
41459         if(tab == this.active || tab.disabled){
41460             return tab;
41461         }
41462         var e = {};
41463         this.fireEvent("beforetabchange", this, e, tab);
41464         if(e.cancel !== true && !tab.disabled){
41465             if(this.active){
41466                 this.active.hide();
41467             }
41468             this.active = this.items[id];
41469             this.active.show();
41470             this.fireEvent("tabchange", this, this.active);
41471         }
41472         return tab;
41473     },
41474
41475     /**
41476      * Gets the active {@link Roo.TabPanelItem}.
41477      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41478      */
41479     getActiveTab : function(){
41480         return this.active;
41481     },
41482
41483     /**
41484      * Updates the tab body element to fit the height of the container element
41485      * for overflow scrolling
41486      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41487      */
41488     syncHeight : function(targetHeight){
41489         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41490         var bm = this.bodyEl.getMargins();
41491         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41492         this.bodyEl.setHeight(newHeight);
41493         return newHeight;
41494     },
41495
41496     onResize : function(){
41497         if(this.monitorResize){
41498             this.autoSizeTabs();
41499         }
41500     },
41501
41502     /**
41503      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41504      */
41505     beginUpdate : function(){
41506         this.updating = true;
41507     },
41508
41509     /**
41510      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41511      */
41512     endUpdate : function(){
41513         this.updating = false;
41514         this.autoSizeTabs();
41515     },
41516
41517     /**
41518      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41519      */
41520     autoSizeTabs : function()
41521     {
41522         var count = this.items.length;
41523         var vcount = count - this.hiddenCount;
41524         
41525         if (vcount < 2) {
41526             this.stripEl.hide();
41527         } else {
41528             this.stripEl.show();
41529         }
41530         
41531         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41532             return;
41533         }
41534         
41535         
41536         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41537         var availWidth = Math.floor(w / vcount);
41538         var b = this.stripBody;
41539         if(b.getWidth() > w){
41540             var tabs = this.items;
41541             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41542             if(availWidth < this.minTabWidth){
41543                 /*if(!this.sleft){    // incomplete scrolling code
41544                     this.createScrollButtons();
41545                 }
41546                 this.showScroll();
41547                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41548             }
41549         }else{
41550             if(this.currentTabWidth < this.preferredTabWidth){
41551                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41552             }
41553         }
41554     },
41555
41556     /**
41557      * Returns the number of tabs in this TabPanel.
41558      * @return {Number}
41559      */
41560      getCount : function(){
41561          return this.items.length;
41562      },
41563
41564     /**
41565      * Resizes all the tabs to the passed width
41566      * @param {Number} The new width
41567      */
41568     setTabWidth : function(width){
41569         this.currentTabWidth = width;
41570         for(var i = 0, len = this.items.length; i < len; i++) {
41571                 if(!this.items[i].isHidden()) {
41572                 this.items[i].setWidth(width);
41573             }
41574         }
41575     },
41576
41577     /**
41578      * Destroys this TabPanel
41579      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41580      */
41581     destroy : function(removeEl){
41582         Roo.EventManager.removeResizeListener(this.onResize, this);
41583         for(var i = 0, len = this.items.length; i < len; i++){
41584             this.items[i].purgeListeners();
41585         }
41586         if(removeEl === true){
41587             this.el.update("");
41588             this.el.remove();
41589         }
41590     },
41591     
41592     createStrip : function(container)
41593     {
41594         var strip = document.createElement("nav");
41595         strip.className = Roo.bootstrap.version == 4 ?
41596             "navbar-light bg-light" : 
41597             "navbar navbar-default"; //"x-tabs-wrap";
41598         container.appendChild(strip);
41599         return strip;
41600     },
41601     
41602     createStripList : function(strip)
41603     {
41604         // div wrapper for retard IE
41605         // returns the "tr" element.
41606         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41607         //'<div class="x-tabs-strip-wrap">'+
41608           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41609           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41610         return strip.firstChild; //.firstChild.firstChild.firstChild;
41611     },
41612     createBody : function(container)
41613     {
41614         var body = document.createElement("div");
41615         Roo.id(body, "tab-body");
41616         //Roo.fly(body).addClass("x-tabs-body");
41617         Roo.fly(body).addClass("tab-content");
41618         container.appendChild(body);
41619         return body;
41620     },
41621     createItemBody :function(bodyEl, id){
41622         var body = Roo.getDom(id);
41623         if(!body){
41624             body = document.createElement("div");
41625             body.id = id;
41626         }
41627         //Roo.fly(body).addClass("x-tabs-item-body");
41628         Roo.fly(body).addClass("tab-pane");
41629          bodyEl.insertBefore(body, bodyEl.firstChild);
41630         return body;
41631     },
41632     /** @private */
41633     createStripElements :  function(stripEl, text, closable, tpl)
41634     {
41635         var td = document.createElement("li"); // was td..
41636         td.className = 'nav-item';
41637         
41638         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41639         
41640         
41641         stripEl.appendChild(td);
41642         /*if(closable){
41643             td.className = "x-tabs-closable";
41644             if(!this.closeTpl){
41645                 this.closeTpl = new Roo.Template(
41646                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41647                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41648                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41649                 );
41650             }
41651             var el = this.closeTpl.overwrite(td, {"text": text});
41652             var close = el.getElementsByTagName("div")[0];
41653             var inner = el.getElementsByTagName("em")[0];
41654             return {"el": el, "close": close, "inner": inner};
41655         } else {
41656         */
41657         // not sure what this is..
41658 //            if(!this.tabTpl){
41659                 //this.tabTpl = new Roo.Template(
41660                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41661                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41662                 //);
41663 //                this.tabTpl = new Roo.Template(
41664 //                   '<a href="#">' +
41665 //                   '<span unselectable="on"' +
41666 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41667 //                            ' >{text}</span></a>'
41668 //                );
41669 //                
41670 //            }
41671
41672
41673             var template = tpl || this.tabTpl || false;
41674             
41675             if(!template){
41676                 template =  new Roo.Template(
41677                         Roo.bootstrap.version == 4 ? 
41678                             (
41679                                 '<a class="nav-link" href="#" unselectable="on"' +
41680                                      (this.disableTooltips ? '' : ' title="{text}"') +
41681                                      ' >{text}</a>'
41682                             ) : (
41683                                 '<a class="nav-link" href="#">' +
41684                                 '<span unselectable="on"' +
41685                                          (this.disableTooltips ? '' : ' title="{text}"') +
41686                                     ' >{text}</span></a>'
41687                             )
41688                 );
41689             }
41690             
41691             switch (typeof(template)) {
41692                 case 'object' :
41693                     break;
41694                 case 'string' :
41695                     template = new Roo.Template(template);
41696                     break;
41697                 default :
41698                     break;
41699             }
41700             
41701             var el = template.overwrite(td, {"text": text});
41702             
41703             var inner = el.getElementsByTagName("span")[0];
41704             
41705             return {"el": el, "inner": inner};
41706             
41707     }
41708         
41709     
41710 });
41711
41712 /**
41713  * @class Roo.TabPanelItem
41714  * @extends Roo.util.Observable
41715  * Represents an individual item (tab plus body) in a TabPanel.
41716  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41717  * @param {String} id The id of this TabPanelItem
41718  * @param {String} text The text for the tab of this TabPanelItem
41719  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41720  */
41721 Roo.bootstrap.panel.TabItem = function(config){
41722     /**
41723      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41724      * @type Roo.TabPanel
41725      */
41726     this.tabPanel = config.panel;
41727     /**
41728      * The id for this TabPanelItem
41729      * @type String
41730      */
41731     this.id = config.id;
41732     /** @private */
41733     this.disabled = false;
41734     /** @private */
41735     this.text = config.text;
41736     /** @private */
41737     this.loaded = false;
41738     this.closable = config.closable;
41739
41740     /**
41741      * The body element for this TabPanelItem.
41742      * @type Roo.Element
41743      */
41744     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41745     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41746     this.bodyEl.setStyle("display", "block");
41747     this.bodyEl.setStyle("zoom", "1");
41748     //this.hideAction();
41749
41750     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41751     /** @private */
41752     this.el = Roo.get(els.el);
41753     this.inner = Roo.get(els.inner, true);
41754      this.textEl = Roo.bootstrap.version == 4 ?
41755         this.el : Roo.get(this.el.dom.firstChild, true);
41756
41757     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41758     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41759
41760     
41761 //    this.el.on("mousedown", this.onTabMouseDown, this);
41762     this.el.on("click", this.onTabClick, this);
41763     /** @private */
41764     if(config.closable){
41765         var c = Roo.get(els.close, true);
41766         c.dom.title = this.closeText;
41767         c.addClassOnOver("close-over");
41768         c.on("click", this.closeClick, this);
41769      }
41770
41771     this.addEvents({
41772          /**
41773          * @event activate
41774          * Fires when this tab becomes the active tab.
41775          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41776          * @param {Roo.TabPanelItem} this
41777          */
41778         "activate": true,
41779         /**
41780          * @event beforeclose
41781          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41782          * @param {Roo.TabPanelItem} this
41783          * @param {Object} e Set cancel to true on this object to cancel the close.
41784          */
41785         "beforeclose": true,
41786         /**
41787          * @event close
41788          * Fires when this tab is closed.
41789          * @param {Roo.TabPanelItem} this
41790          */
41791          "close": true,
41792         /**
41793          * @event deactivate
41794          * Fires when this tab is no longer the active tab.
41795          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41796          * @param {Roo.TabPanelItem} this
41797          */
41798          "deactivate" : true
41799     });
41800     this.hidden = false;
41801
41802     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41803 };
41804
41805 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41806            {
41807     purgeListeners : function(){
41808        Roo.util.Observable.prototype.purgeListeners.call(this);
41809        this.el.removeAllListeners();
41810     },
41811     /**
41812      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41813      */
41814     show : function(){
41815         this.status_node.addClass("active");
41816         this.showAction();
41817         if(Roo.isOpera){
41818             this.tabPanel.stripWrap.repaint();
41819         }
41820         this.fireEvent("activate", this.tabPanel, this);
41821     },
41822
41823     /**
41824      * Returns true if this tab is the active tab.
41825      * @return {Boolean}
41826      */
41827     isActive : function(){
41828         return this.tabPanel.getActiveTab() == this;
41829     },
41830
41831     /**
41832      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41833      */
41834     hide : function(){
41835         this.status_node.removeClass("active");
41836         this.hideAction();
41837         this.fireEvent("deactivate", this.tabPanel, this);
41838     },
41839
41840     hideAction : function(){
41841         this.bodyEl.hide();
41842         this.bodyEl.setStyle("position", "absolute");
41843         this.bodyEl.setLeft("-20000px");
41844         this.bodyEl.setTop("-20000px");
41845     },
41846
41847     showAction : function(){
41848         this.bodyEl.setStyle("position", "relative");
41849         this.bodyEl.setTop("");
41850         this.bodyEl.setLeft("");
41851         this.bodyEl.show();
41852     },
41853
41854     /**
41855      * Set the tooltip for the tab.
41856      * @param {String} tooltip The tab's tooltip
41857      */
41858     setTooltip : function(text){
41859         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41860             this.textEl.dom.qtip = text;
41861             this.textEl.dom.removeAttribute('title');
41862         }else{
41863             this.textEl.dom.title = text;
41864         }
41865     },
41866
41867     onTabClick : function(e){
41868         e.preventDefault();
41869         this.tabPanel.activate(this.id);
41870     },
41871
41872     onTabMouseDown : function(e){
41873         e.preventDefault();
41874         this.tabPanel.activate(this.id);
41875     },
41876 /*
41877     getWidth : function(){
41878         return this.inner.getWidth();
41879     },
41880
41881     setWidth : function(width){
41882         var iwidth = width - this.linode.getPadding("lr");
41883         this.inner.setWidth(iwidth);
41884         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41885         this.linode.setWidth(width);
41886     },
41887 */
41888     /**
41889      * Show or hide the tab
41890      * @param {Boolean} hidden True to hide or false to show.
41891      */
41892     setHidden : function(hidden){
41893         this.hidden = hidden;
41894         this.linode.setStyle("display", hidden ? "none" : "");
41895     },
41896
41897     /**
41898      * Returns true if this tab is "hidden"
41899      * @return {Boolean}
41900      */
41901     isHidden : function(){
41902         return this.hidden;
41903     },
41904
41905     /**
41906      * Returns the text for this tab
41907      * @return {String}
41908      */
41909     getText : function(){
41910         return this.text;
41911     },
41912     /*
41913     autoSize : function(){
41914         //this.el.beginMeasure();
41915         this.textEl.setWidth(1);
41916         /*
41917          *  #2804 [new] Tabs in Roojs
41918          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41919          */
41920         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41921         //this.el.endMeasure();
41922     //},
41923
41924     /**
41925      * Sets the text for the tab (Note: this also sets the tooltip text)
41926      * @param {String} text The tab's text and tooltip
41927      */
41928     setText : function(text){
41929         this.text = text;
41930         this.textEl.update(text);
41931         this.setTooltip(text);
41932         //if(!this.tabPanel.resizeTabs){
41933         //    this.autoSize();
41934         //}
41935     },
41936     /**
41937      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41938      */
41939     activate : function(){
41940         this.tabPanel.activate(this.id);
41941     },
41942
41943     /**
41944      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41945      */
41946     disable : function(){
41947         if(this.tabPanel.active != this){
41948             this.disabled = true;
41949             this.status_node.addClass("disabled");
41950         }
41951     },
41952
41953     /**
41954      * Enables this TabPanelItem if it was previously disabled.
41955      */
41956     enable : function(){
41957         this.disabled = false;
41958         this.status_node.removeClass("disabled");
41959     },
41960
41961     /**
41962      * Sets the content for this TabPanelItem.
41963      * @param {String} content The content
41964      * @param {Boolean} loadScripts true to look for and load scripts
41965      */
41966     setContent : function(content, loadScripts){
41967         this.bodyEl.update(content, loadScripts);
41968     },
41969
41970     /**
41971      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41972      * @return {Roo.UpdateManager} The UpdateManager
41973      */
41974     getUpdateManager : function(){
41975         return this.bodyEl.getUpdateManager();
41976     },
41977
41978     /**
41979      * Set a URL to be used to load the content for this TabPanelItem.
41980      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41981      * @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)
41982      * @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)
41983      * @return {Roo.UpdateManager} The UpdateManager
41984      */
41985     setUrl : function(url, params, loadOnce){
41986         if(this.refreshDelegate){
41987             this.un('activate', this.refreshDelegate);
41988         }
41989         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41990         this.on("activate", this.refreshDelegate);
41991         return this.bodyEl.getUpdateManager();
41992     },
41993
41994     /** @private */
41995     _handleRefresh : function(url, params, loadOnce){
41996         if(!loadOnce || !this.loaded){
41997             var updater = this.bodyEl.getUpdateManager();
41998             updater.update(url, params, this._setLoaded.createDelegate(this));
41999         }
42000     },
42001
42002     /**
42003      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42004      *   Will fail silently if the setUrl method has not been called.
42005      *   This does not activate the panel, just updates its content.
42006      */
42007     refresh : function(){
42008         if(this.refreshDelegate){
42009            this.loaded = false;
42010            this.refreshDelegate();
42011         }
42012     },
42013
42014     /** @private */
42015     _setLoaded : function(){
42016         this.loaded = true;
42017     },
42018
42019     /** @private */
42020     closeClick : function(e){
42021         var o = {};
42022         e.stopEvent();
42023         this.fireEvent("beforeclose", this, o);
42024         if(o.cancel !== true){
42025             this.tabPanel.removeTab(this.id);
42026         }
42027     },
42028     /**
42029      * The text displayed in the tooltip for the close icon.
42030      * @type String
42031      */
42032     closeText : "Close this tab"
42033 });
42034 /**
42035 *    This script refer to:
42036 *    Title: International Telephone Input
42037 *    Author: Jack O'Connor
42038 *    Code version:  v12.1.12
42039 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42040 **/
42041
42042 Roo.bootstrap.form.PhoneInputData = function() {
42043     var d = [
42044       [
42045         "Afghanistan (‫افغانستان‬‎)",
42046         "af",
42047         "93"
42048       ],
42049       [
42050         "Albania (Shqipëri)",
42051         "al",
42052         "355"
42053       ],
42054       [
42055         "Algeria (‫الجزائر‬‎)",
42056         "dz",
42057         "213"
42058       ],
42059       [
42060         "American Samoa",
42061         "as",
42062         "1684"
42063       ],
42064       [
42065         "Andorra",
42066         "ad",
42067         "376"
42068       ],
42069       [
42070         "Angola",
42071         "ao",
42072         "244"
42073       ],
42074       [
42075         "Anguilla",
42076         "ai",
42077         "1264"
42078       ],
42079       [
42080         "Antigua and Barbuda",
42081         "ag",
42082         "1268"
42083       ],
42084       [
42085         "Argentina",
42086         "ar",
42087         "54"
42088       ],
42089       [
42090         "Armenia (Հայաստան)",
42091         "am",
42092         "374"
42093       ],
42094       [
42095         "Aruba",
42096         "aw",
42097         "297"
42098       ],
42099       [
42100         "Australia",
42101         "au",
42102         "61",
42103         0
42104       ],
42105       [
42106         "Austria (Österreich)",
42107         "at",
42108         "43"
42109       ],
42110       [
42111         "Azerbaijan (Azərbaycan)",
42112         "az",
42113         "994"
42114       ],
42115       [
42116         "Bahamas",
42117         "bs",
42118         "1242"
42119       ],
42120       [
42121         "Bahrain (‫البحرين‬‎)",
42122         "bh",
42123         "973"
42124       ],
42125       [
42126         "Bangladesh (বাংলাদেশ)",
42127         "bd",
42128         "880"
42129       ],
42130       [
42131         "Barbados",
42132         "bb",
42133         "1246"
42134       ],
42135       [
42136         "Belarus (Беларусь)",
42137         "by",
42138         "375"
42139       ],
42140       [
42141         "Belgium (België)",
42142         "be",
42143         "32"
42144       ],
42145       [
42146         "Belize",
42147         "bz",
42148         "501"
42149       ],
42150       [
42151         "Benin (Bénin)",
42152         "bj",
42153         "229"
42154       ],
42155       [
42156         "Bermuda",
42157         "bm",
42158         "1441"
42159       ],
42160       [
42161         "Bhutan (འབྲུག)",
42162         "bt",
42163         "975"
42164       ],
42165       [
42166         "Bolivia",
42167         "bo",
42168         "591"
42169       ],
42170       [
42171         "Bosnia and Herzegovina (Босна и Херцеговина)",
42172         "ba",
42173         "387"
42174       ],
42175       [
42176         "Botswana",
42177         "bw",
42178         "267"
42179       ],
42180       [
42181         "Brazil (Brasil)",
42182         "br",
42183         "55"
42184       ],
42185       [
42186         "British Indian Ocean Territory",
42187         "io",
42188         "246"
42189       ],
42190       [
42191         "British Virgin Islands",
42192         "vg",
42193         "1284"
42194       ],
42195       [
42196         "Brunei",
42197         "bn",
42198         "673"
42199       ],
42200       [
42201         "Bulgaria (България)",
42202         "bg",
42203         "359"
42204       ],
42205       [
42206         "Burkina Faso",
42207         "bf",
42208         "226"
42209       ],
42210       [
42211         "Burundi (Uburundi)",
42212         "bi",
42213         "257"
42214       ],
42215       [
42216         "Cambodia (កម្ពុជា)",
42217         "kh",
42218         "855"
42219       ],
42220       [
42221         "Cameroon (Cameroun)",
42222         "cm",
42223         "237"
42224       ],
42225       [
42226         "Canada",
42227         "ca",
42228         "1",
42229         1,
42230         ["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"]
42231       ],
42232       [
42233         "Cape Verde (Kabu Verdi)",
42234         "cv",
42235         "238"
42236       ],
42237       [
42238         "Caribbean Netherlands",
42239         "bq",
42240         "599",
42241         1
42242       ],
42243       [
42244         "Cayman Islands",
42245         "ky",
42246         "1345"
42247       ],
42248       [
42249         "Central African Republic (République centrafricaine)",
42250         "cf",
42251         "236"
42252       ],
42253       [
42254         "Chad (Tchad)",
42255         "td",
42256         "235"
42257       ],
42258       [
42259         "Chile",
42260         "cl",
42261         "56"
42262       ],
42263       [
42264         "China (中国)",
42265         "cn",
42266         "86"
42267       ],
42268       [
42269         "Christmas Island",
42270         "cx",
42271         "61",
42272         2
42273       ],
42274       [
42275         "Cocos (Keeling) Islands",
42276         "cc",
42277         "61",
42278         1
42279       ],
42280       [
42281         "Colombia",
42282         "co",
42283         "57"
42284       ],
42285       [
42286         "Comoros (‫جزر القمر‬‎)",
42287         "km",
42288         "269"
42289       ],
42290       [
42291         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42292         "cd",
42293         "243"
42294       ],
42295       [
42296         "Congo (Republic) (Congo-Brazzaville)",
42297         "cg",
42298         "242"
42299       ],
42300       [
42301         "Cook Islands",
42302         "ck",
42303         "682"
42304       ],
42305       [
42306         "Costa Rica",
42307         "cr",
42308         "506"
42309       ],
42310       [
42311         "Côte d’Ivoire",
42312         "ci",
42313         "225"
42314       ],
42315       [
42316         "Croatia (Hrvatska)",
42317         "hr",
42318         "385"
42319       ],
42320       [
42321         "Cuba",
42322         "cu",
42323         "53"
42324       ],
42325       [
42326         "Curaçao",
42327         "cw",
42328         "599",
42329         0
42330       ],
42331       [
42332         "Cyprus (Κύπρος)",
42333         "cy",
42334         "357"
42335       ],
42336       [
42337         "Czech Republic (Česká republika)",
42338         "cz",
42339         "420"
42340       ],
42341       [
42342         "Denmark (Danmark)",
42343         "dk",
42344         "45"
42345       ],
42346       [
42347         "Djibouti",
42348         "dj",
42349         "253"
42350       ],
42351       [
42352         "Dominica",
42353         "dm",
42354         "1767"
42355       ],
42356       [
42357         "Dominican Republic (República Dominicana)",
42358         "do",
42359         "1",
42360         2,
42361         ["809", "829", "849"]
42362       ],
42363       [
42364         "Ecuador",
42365         "ec",
42366         "593"
42367       ],
42368       [
42369         "Egypt (‫مصر‬‎)",
42370         "eg",
42371         "20"
42372       ],
42373       [
42374         "El Salvador",
42375         "sv",
42376         "503"
42377       ],
42378       [
42379         "Equatorial Guinea (Guinea Ecuatorial)",
42380         "gq",
42381         "240"
42382       ],
42383       [
42384         "Eritrea",
42385         "er",
42386         "291"
42387       ],
42388       [
42389         "Estonia (Eesti)",
42390         "ee",
42391         "372"
42392       ],
42393       [
42394         "Ethiopia",
42395         "et",
42396         "251"
42397       ],
42398       [
42399         "Falkland Islands (Islas Malvinas)",
42400         "fk",
42401         "500"
42402       ],
42403       [
42404         "Faroe Islands (Føroyar)",
42405         "fo",
42406         "298"
42407       ],
42408       [
42409         "Fiji",
42410         "fj",
42411         "679"
42412       ],
42413       [
42414         "Finland (Suomi)",
42415         "fi",
42416         "358",
42417         0
42418       ],
42419       [
42420         "France",
42421         "fr",
42422         "33"
42423       ],
42424       [
42425         "French Guiana (Guyane française)",
42426         "gf",
42427         "594"
42428       ],
42429       [
42430         "French Polynesia (Polynésie française)",
42431         "pf",
42432         "689"
42433       ],
42434       [
42435         "Gabon",
42436         "ga",
42437         "241"
42438       ],
42439       [
42440         "Gambia",
42441         "gm",
42442         "220"
42443       ],
42444       [
42445         "Georgia (საქართველო)",
42446         "ge",
42447         "995"
42448       ],
42449       [
42450         "Germany (Deutschland)",
42451         "de",
42452         "49"
42453       ],
42454       [
42455         "Ghana (Gaana)",
42456         "gh",
42457         "233"
42458       ],
42459       [
42460         "Gibraltar",
42461         "gi",
42462         "350"
42463       ],
42464       [
42465         "Greece (Ελλάδα)",
42466         "gr",
42467         "30"
42468       ],
42469       [
42470         "Greenland (Kalaallit Nunaat)",
42471         "gl",
42472         "299"
42473       ],
42474       [
42475         "Grenada",
42476         "gd",
42477         "1473"
42478       ],
42479       [
42480         "Guadeloupe",
42481         "gp",
42482         "590",
42483         0
42484       ],
42485       [
42486         "Guam",
42487         "gu",
42488         "1671"
42489       ],
42490       [
42491         "Guatemala",
42492         "gt",
42493         "502"
42494       ],
42495       [
42496         "Guernsey",
42497         "gg",
42498         "44",
42499         1
42500       ],
42501       [
42502         "Guinea (Guinée)",
42503         "gn",
42504         "224"
42505       ],
42506       [
42507         "Guinea-Bissau (Guiné Bissau)",
42508         "gw",
42509         "245"
42510       ],
42511       [
42512         "Guyana",
42513         "gy",
42514         "592"
42515       ],
42516       [
42517         "Haiti",
42518         "ht",
42519         "509"
42520       ],
42521       [
42522         "Honduras",
42523         "hn",
42524         "504"
42525       ],
42526       [
42527         "Hong Kong (香港)",
42528         "hk",
42529         "852"
42530       ],
42531       [
42532         "Hungary (Magyarország)",
42533         "hu",
42534         "36"
42535       ],
42536       [
42537         "Iceland (Ísland)",
42538         "is",
42539         "354"
42540       ],
42541       [
42542         "India (भारत)",
42543         "in",
42544         "91"
42545       ],
42546       [
42547         "Indonesia",
42548         "id",
42549         "62"
42550       ],
42551       [
42552         "Iran (‫ایران‬‎)",
42553         "ir",
42554         "98"
42555       ],
42556       [
42557         "Iraq (‫العراق‬‎)",
42558         "iq",
42559         "964"
42560       ],
42561       [
42562         "Ireland",
42563         "ie",
42564         "353"
42565       ],
42566       [
42567         "Isle of Man",
42568         "im",
42569         "44",
42570         2
42571       ],
42572       [
42573         "Israel (‫ישראל‬‎)",
42574         "il",
42575         "972"
42576       ],
42577       [
42578         "Italy (Italia)",
42579         "it",
42580         "39",
42581         0
42582       ],
42583       [
42584         "Jamaica",
42585         "jm",
42586         "1876"
42587       ],
42588       [
42589         "Japan (日本)",
42590         "jp",
42591         "81"
42592       ],
42593       [
42594         "Jersey",
42595         "je",
42596         "44",
42597         3
42598       ],
42599       [
42600         "Jordan (‫الأردن‬‎)",
42601         "jo",
42602         "962"
42603       ],
42604       [
42605         "Kazakhstan (Казахстан)",
42606         "kz",
42607         "7",
42608         1
42609       ],
42610       [
42611         "Kenya",
42612         "ke",
42613         "254"
42614       ],
42615       [
42616         "Kiribati",
42617         "ki",
42618         "686"
42619       ],
42620       [
42621         "Kosovo",
42622         "xk",
42623         "383"
42624       ],
42625       [
42626         "Kuwait (‫الكويت‬‎)",
42627         "kw",
42628         "965"
42629       ],
42630       [
42631         "Kyrgyzstan (Кыргызстан)",
42632         "kg",
42633         "996"
42634       ],
42635       [
42636         "Laos (ລາວ)",
42637         "la",
42638         "856"
42639       ],
42640       [
42641         "Latvia (Latvija)",
42642         "lv",
42643         "371"
42644       ],
42645       [
42646         "Lebanon (‫لبنان‬‎)",
42647         "lb",
42648         "961"
42649       ],
42650       [
42651         "Lesotho",
42652         "ls",
42653         "266"
42654       ],
42655       [
42656         "Liberia",
42657         "lr",
42658         "231"
42659       ],
42660       [
42661         "Libya (‫ليبيا‬‎)",
42662         "ly",
42663         "218"
42664       ],
42665       [
42666         "Liechtenstein",
42667         "li",
42668         "423"
42669       ],
42670       [
42671         "Lithuania (Lietuva)",
42672         "lt",
42673         "370"
42674       ],
42675       [
42676         "Luxembourg",
42677         "lu",
42678         "352"
42679       ],
42680       [
42681         "Macau (澳門)",
42682         "mo",
42683         "853"
42684       ],
42685       [
42686         "Macedonia (FYROM) (Македонија)",
42687         "mk",
42688         "389"
42689       ],
42690       [
42691         "Madagascar (Madagasikara)",
42692         "mg",
42693         "261"
42694       ],
42695       [
42696         "Malawi",
42697         "mw",
42698         "265"
42699       ],
42700       [
42701         "Malaysia",
42702         "my",
42703         "60"
42704       ],
42705       [
42706         "Maldives",
42707         "mv",
42708         "960"
42709       ],
42710       [
42711         "Mali",
42712         "ml",
42713         "223"
42714       ],
42715       [
42716         "Malta",
42717         "mt",
42718         "356"
42719       ],
42720       [
42721         "Marshall Islands",
42722         "mh",
42723         "692"
42724       ],
42725       [
42726         "Martinique",
42727         "mq",
42728         "596"
42729       ],
42730       [
42731         "Mauritania (‫موريتانيا‬‎)",
42732         "mr",
42733         "222"
42734       ],
42735       [
42736         "Mauritius (Moris)",
42737         "mu",
42738         "230"
42739       ],
42740       [
42741         "Mayotte",
42742         "yt",
42743         "262",
42744         1
42745       ],
42746       [
42747         "Mexico (México)",
42748         "mx",
42749         "52"
42750       ],
42751       [
42752         "Micronesia",
42753         "fm",
42754         "691"
42755       ],
42756       [
42757         "Moldova (Republica Moldova)",
42758         "md",
42759         "373"
42760       ],
42761       [
42762         "Monaco",
42763         "mc",
42764         "377"
42765       ],
42766       [
42767         "Mongolia (Монгол)",
42768         "mn",
42769         "976"
42770       ],
42771       [
42772         "Montenegro (Crna Gora)",
42773         "me",
42774         "382"
42775       ],
42776       [
42777         "Montserrat",
42778         "ms",
42779         "1664"
42780       ],
42781       [
42782         "Morocco (‫المغرب‬‎)",
42783         "ma",
42784         "212",
42785         0
42786       ],
42787       [
42788         "Mozambique (Moçambique)",
42789         "mz",
42790         "258"
42791       ],
42792       [
42793         "Myanmar (Burma) (မြန်မာ)",
42794         "mm",
42795         "95"
42796       ],
42797       [
42798         "Namibia (Namibië)",
42799         "na",
42800         "264"
42801       ],
42802       [
42803         "Nauru",
42804         "nr",
42805         "674"
42806       ],
42807       [
42808         "Nepal (नेपाल)",
42809         "np",
42810         "977"
42811       ],
42812       [
42813         "Netherlands (Nederland)",
42814         "nl",
42815         "31"
42816       ],
42817       [
42818         "New Caledonia (Nouvelle-Calédonie)",
42819         "nc",
42820         "687"
42821       ],
42822       [
42823         "New Zealand",
42824         "nz",
42825         "64"
42826       ],
42827       [
42828         "Nicaragua",
42829         "ni",
42830         "505"
42831       ],
42832       [
42833         "Niger (Nijar)",
42834         "ne",
42835         "227"
42836       ],
42837       [
42838         "Nigeria",
42839         "ng",
42840         "234"
42841       ],
42842       [
42843         "Niue",
42844         "nu",
42845         "683"
42846       ],
42847       [
42848         "Norfolk Island",
42849         "nf",
42850         "672"
42851       ],
42852       [
42853         "North Korea (조선 민주주의 인민 공화국)",
42854         "kp",
42855         "850"
42856       ],
42857       [
42858         "Northern Mariana Islands",
42859         "mp",
42860         "1670"
42861       ],
42862       [
42863         "Norway (Norge)",
42864         "no",
42865         "47",
42866         0
42867       ],
42868       [
42869         "Oman (‫عُمان‬‎)",
42870         "om",
42871         "968"
42872       ],
42873       [
42874         "Pakistan (‫پاکستان‬‎)",
42875         "pk",
42876         "92"
42877       ],
42878       [
42879         "Palau",
42880         "pw",
42881         "680"
42882       ],
42883       [
42884         "Palestine (‫فلسطين‬‎)",
42885         "ps",
42886         "970"
42887       ],
42888       [
42889         "Panama (Panamá)",
42890         "pa",
42891         "507"
42892       ],
42893       [
42894         "Papua New Guinea",
42895         "pg",
42896         "675"
42897       ],
42898       [
42899         "Paraguay",
42900         "py",
42901         "595"
42902       ],
42903       [
42904         "Peru (Perú)",
42905         "pe",
42906         "51"
42907       ],
42908       [
42909         "Philippines",
42910         "ph",
42911         "63"
42912       ],
42913       [
42914         "Poland (Polska)",
42915         "pl",
42916         "48"
42917       ],
42918       [
42919         "Portugal",
42920         "pt",
42921         "351"
42922       ],
42923       [
42924         "Puerto Rico",
42925         "pr",
42926         "1",
42927         3,
42928         ["787", "939"]
42929       ],
42930       [
42931         "Qatar (‫قطر‬‎)",
42932         "qa",
42933         "974"
42934       ],
42935       [
42936         "Réunion (La Réunion)",
42937         "re",
42938         "262",
42939         0
42940       ],
42941       [
42942         "Romania (România)",
42943         "ro",
42944         "40"
42945       ],
42946       [
42947         "Russia (Россия)",
42948         "ru",
42949         "7",
42950         0
42951       ],
42952       [
42953         "Rwanda",
42954         "rw",
42955         "250"
42956       ],
42957       [
42958         "Saint Barthélemy",
42959         "bl",
42960         "590",
42961         1
42962       ],
42963       [
42964         "Saint Helena",
42965         "sh",
42966         "290"
42967       ],
42968       [
42969         "Saint Kitts and Nevis",
42970         "kn",
42971         "1869"
42972       ],
42973       [
42974         "Saint Lucia",
42975         "lc",
42976         "1758"
42977       ],
42978       [
42979         "Saint Martin (Saint-Martin (partie française))",
42980         "mf",
42981         "590",
42982         2
42983       ],
42984       [
42985         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42986         "pm",
42987         "508"
42988       ],
42989       [
42990         "Saint Vincent and the Grenadines",
42991         "vc",
42992         "1784"
42993       ],
42994       [
42995         "Samoa",
42996         "ws",
42997         "685"
42998       ],
42999       [
43000         "San Marino",
43001         "sm",
43002         "378"
43003       ],
43004       [
43005         "São Tomé and Príncipe (São Tomé e Príncipe)",
43006         "st",
43007         "239"
43008       ],
43009       [
43010         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43011         "sa",
43012         "966"
43013       ],
43014       [
43015         "Senegal (Sénégal)",
43016         "sn",
43017         "221"
43018       ],
43019       [
43020         "Serbia (Србија)",
43021         "rs",
43022         "381"
43023       ],
43024       [
43025         "Seychelles",
43026         "sc",
43027         "248"
43028       ],
43029       [
43030         "Sierra Leone",
43031         "sl",
43032         "232"
43033       ],
43034       [
43035         "Singapore",
43036         "sg",
43037         "65"
43038       ],
43039       [
43040         "Sint Maarten",
43041         "sx",
43042         "1721"
43043       ],
43044       [
43045         "Slovakia (Slovensko)",
43046         "sk",
43047         "421"
43048       ],
43049       [
43050         "Slovenia (Slovenija)",
43051         "si",
43052         "386"
43053       ],
43054       [
43055         "Solomon Islands",
43056         "sb",
43057         "677"
43058       ],
43059       [
43060         "Somalia (Soomaaliya)",
43061         "so",
43062         "252"
43063       ],
43064       [
43065         "South Africa",
43066         "za",
43067         "27"
43068       ],
43069       [
43070         "South Korea (대한민국)",
43071         "kr",
43072         "82"
43073       ],
43074       [
43075         "South Sudan (‫جنوب السودان‬‎)",
43076         "ss",
43077         "211"
43078       ],
43079       [
43080         "Spain (España)",
43081         "es",
43082         "34"
43083       ],
43084       [
43085         "Sri Lanka (ශ්‍රී ලංකාව)",
43086         "lk",
43087         "94"
43088       ],
43089       [
43090         "Sudan (‫السودان‬‎)",
43091         "sd",
43092         "249"
43093       ],
43094       [
43095         "Suriname",
43096         "sr",
43097         "597"
43098       ],
43099       [
43100         "Svalbard and Jan Mayen",
43101         "sj",
43102         "47",
43103         1
43104       ],
43105       [
43106         "Swaziland",
43107         "sz",
43108         "268"
43109       ],
43110       [
43111         "Sweden (Sverige)",
43112         "se",
43113         "46"
43114       ],
43115       [
43116         "Switzerland (Schweiz)",
43117         "ch",
43118         "41"
43119       ],
43120       [
43121         "Syria (‫سوريا‬‎)",
43122         "sy",
43123         "963"
43124       ],
43125       [
43126         "Taiwan (台灣)",
43127         "tw",
43128         "886"
43129       ],
43130       [
43131         "Tajikistan",
43132         "tj",
43133         "992"
43134       ],
43135       [
43136         "Tanzania",
43137         "tz",
43138         "255"
43139       ],
43140       [
43141         "Thailand (ไทย)",
43142         "th",
43143         "66"
43144       ],
43145       [
43146         "Timor-Leste",
43147         "tl",
43148         "670"
43149       ],
43150       [
43151         "Togo",
43152         "tg",
43153         "228"
43154       ],
43155       [
43156         "Tokelau",
43157         "tk",
43158         "690"
43159       ],
43160       [
43161         "Tonga",
43162         "to",
43163         "676"
43164       ],
43165       [
43166         "Trinidad and Tobago",
43167         "tt",
43168         "1868"
43169       ],
43170       [
43171         "Tunisia (‫تونس‬‎)",
43172         "tn",
43173         "216"
43174       ],
43175       [
43176         "Turkey (Türkiye)",
43177         "tr",
43178         "90"
43179       ],
43180       [
43181         "Turkmenistan",
43182         "tm",
43183         "993"
43184       ],
43185       [
43186         "Turks and Caicos Islands",
43187         "tc",
43188         "1649"
43189       ],
43190       [
43191         "Tuvalu",
43192         "tv",
43193         "688"
43194       ],
43195       [
43196         "U.S. Virgin Islands",
43197         "vi",
43198         "1340"
43199       ],
43200       [
43201         "Uganda",
43202         "ug",
43203         "256"
43204       ],
43205       [
43206         "Ukraine (Україна)",
43207         "ua",
43208         "380"
43209       ],
43210       [
43211         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43212         "ae",
43213         "971"
43214       ],
43215       [
43216         "United Kingdom",
43217         "gb",
43218         "44",
43219         0
43220       ],
43221       [
43222         "United States",
43223         "us",
43224         "1",
43225         0
43226       ],
43227       [
43228         "Uruguay",
43229         "uy",
43230         "598"
43231       ],
43232       [
43233         "Uzbekistan (Oʻzbekiston)",
43234         "uz",
43235         "998"
43236       ],
43237       [
43238         "Vanuatu",
43239         "vu",
43240         "678"
43241       ],
43242       [
43243         "Vatican City (Città del Vaticano)",
43244         "va",
43245         "39",
43246         1
43247       ],
43248       [
43249         "Venezuela",
43250         "ve",
43251         "58"
43252       ],
43253       [
43254         "Vietnam (Việt Nam)",
43255         "vn",
43256         "84"
43257       ],
43258       [
43259         "Wallis and Futuna (Wallis-et-Futuna)",
43260         "wf",
43261         "681"
43262       ],
43263       [
43264         "Western Sahara (‫الصحراء الغربية‬‎)",
43265         "eh",
43266         "212",
43267         1
43268       ],
43269       [
43270         "Yemen (‫اليمن‬‎)",
43271         "ye",
43272         "967"
43273       ],
43274       [
43275         "Zambia",
43276         "zm",
43277         "260"
43278       ],
43279       [
43280         "Zimbabwe",
43281         "zw",
43282         "263"
43283       ],
43284       [
43285         "Åland Islands",
43286         "ax",
43287         "358",
43288         1
43289       ]
43290   ];
43291   
43292   return d;
43293 }/**
43294 *    This script refer to:
43295 *    Title: International Telephone Input
43296 *    Author: Jack O'Connor
43297 *    Code version:  v12.1.12
43298 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43299 **/
43300
43301 /**
43302  * @class Roo.bootstrap.form.PhoneInput
43303  * @extends Roo.bootstrap.form.TriggerField
43304  * An input with International dial-code selection
43305  
43306  * @cfg {String} defaultDialCode default '+852'
43307  * @cfg {Array} preferedCountries default []
43308   
43309  * @constructor
43310  * Create a new PhoneInput.
43311  * @param {Object} config Configuration options
43312  */
43313
43314 Roo.bootstrap.form.PhoneInput = function(config) {
43315     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43316 };
43317
43318 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43319         /**
43320         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43321         */
43322         listWidth: undefined,
43323         
43324         selectedClass: 'active',
43325         
43326         invalidClass : "has-warning",
43327         
43328         validClass: 'has-success',
43329         
43330         allowed: '0123456789',
43331         
43332         max_length: 15,
43333         
43334         /**
43335          * @cfg {String} defaultDialCode The default dial code when initializing the input
43336          */
43337         defaultDialCode: '+852',
43338         
43339         /**
43340          * @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
43341          */
43342         preferedCountries: false,
43343         
43344         getAutoCreate : function()
43345         {
43346             var data = Roo.bootstrap.form.PhoneInputData();
43347             var align = this.labelAlign || this.parentLabelAlign();
43348             var id = Roo.id();
43349             
43350             this.allCountries = [];
43351             this.dialCodeMapping = [];
43352             
43353             for (var i = 0; i < data.length; i++) {
43354               var c = data[i];
43355               this.allCountries[i] = {
43356                 name: c[0],
43357                 iso2: c[1],
43358                 dialCode: c[2],
43359                 priority: c[3] || 0,
43360                 areaCodes: c[4] || null
43361               };
43362               this.dialCodeMapping[c[2]] = {
43363                   name: c[0],
43364                   iso2: c[1],
43365                   priority: c[3] || 0,
43366                   areaCodes: c[4] || null
43367               };
43368             }
43369             
43370             var cfg = {
43371                 cls: 'form-group',
43372                 cn: []
43373             };
43374             
43375             var input =  {
43376                 tag: 'input',
43377                 id : id,
43378                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43379                 maxlength: this.max_length,
43380                 cls : 'form-control tel-input',
43381                 autocomplete: 'new-password'
43382             };
43383             
43384             var hiddenInput = {
43385                 tag: 'input',
43386                 type: 'hidden',
43387                 cls: 'hidden-tel-input'
43388             };
43389             
43390             if (this.name) {
43391                 hiddenInput.name = this.name;
43392             }
43393             
43394             if (this.disabled) {
43395                 input.disabled = true;
43396             }
43397             
43398             var flag_container = {
43399                 tag: 'div',
43400                 cls: 'flag-box',
43401                 cn: [
43402                     {
43403                         tag: 'div',
43404                         cls: 'flag'
43405                     },
43406                     {
43407                         tag: 'div',
43408                         cls: 'caret'
43409                     }
43410                 ]
43411             };
43412             
43413             var box = {
43414                 tag: 'div',
43415                 cls: this.hasFeedback ? 'has-feedback' : '',
43416                 cn: [
43417                     hiddenInput,
43418                     input,
43419                     {
43420                         tag: 'input',
43421                         cls: 'dial-code-holder',
43422                         disabled: true
43423                     }
43424                 ]
43425             };
43426             
43427             var container = {
43428                 cls: 'roo-select2-container input-group',
43429                 cn: [
43430                     flag_container,
43431                     box
43432                 ]
43433             };
43434             
43435             if (this.fieldLabel.length) {
43436                 var indicator = {
43437                     tag: 'i',
43438                     tooltip: 'This field is required'
43439                 };
43440                 
43441                 var label = {
43442                     tag: 'label',
43443                     'for':  id,
43444                     cls: 'control-label',
43445                     cn: []
43446                 };
43447                 
43448                 var label_text = {
43449                     tag: 'span',
43450                     html: this.fieldLabel
43451                 };
43452                 
43453                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43454                 label.cn = [
43455                     indicator,
43456                     label_text
43457                 ];
43458                 
43459                 if(this.indicatorpos == 'right') {
43460                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43461                     label.cn = [
43462                         label_text,
43463                         indicator
43464                     ];
43465                 }
43466                 
43467                 if(align == 'left') {
43468                     container = {
43469                         tag: 'div',
43470                         cn: [
43471                             container
43472                         ]
43473                     };
43474                     
43475                     if(this.labelWidth > 12){
43476                         label.style = "width: " + this.labelWidth + 'px';
43477                     }
43478                     if(this.labelWidth < 13 && this.labelmd == 0){
43479                         this.labelmd = this.labelWidth;
43480                     }
43481                     if(this.labellg > 0){
43482                         label.cls += ' col-lg-' + this.labellg;
43483                         input.cls += ' col-lg-' + (12 - this.labellg);
43484                     }
43485                     if(this.labelmd > 0){
43486                         label.cls += ' col-md-' + this.labelmd;
43487                         container.cls += ' col-md-' + (12 - this.labelmd);
43488                     }
43489                     if(this.labelsm > 0){
43490                         label.cls += ' col-sm-' + this.labelsm;
43491                         container.cls += ' col-sm-' + (12 - this.labelsm);
43492                     }
43493                     if(this.labelxs > 0){
43494                         label.cls += ' col-xs-' + this.labelxs;
43495                         container.cls += ' col-xs-' + (12 - this.labelxs);
43496                     }
43497                 }
43498             }
43499             
43500             cfg.cn = [
43501                 label,
43502                 container
43503             ];
43504             
43505             var settings = this;
43506             
43507             ['xs','sm','md','lg'].map(function(size){
43508                 if (settings[size]) {
43509                     cfg.cls += ' col-' + size + '-' + settings[size];
43510                 }
43511             });
43512             
43513             this.store = new Roo.data.Store({
43514                 proxy : new Roo.data.MemoryProxy({}),
43515                 reader : new Roo.data.JsonReader({
43516                     fields : [
43517                         {
43518                             'name' : 'name',
43519                             'type' : 'string'
43520                         },
43521                         {
43522                             'name' : 'iso2',
43523                             'type' : 'string'
43524                         },
43525                         {
43526                             'name' : 'dialCode',
43527                             'type' : 'string'
43528                         },
43529                         {
43530                             'name' : 'priority',
43531                             'type' : 'string'
43532                         },
43533                         {
43534                             'name' : 'areaCodes',
43535                             'type' : 'string'
43536                         }
43537                     ]
43538                 })
43539             });
43540             
43541             if(!this.preferedCountries) {
43542                 this.preferedCountries = [
43543                     'hk',
43544                     'gb',
43545                     'us'
43546                 ];
43547             }
43548             
43549             var p = this.preferedCountries.reverse();
43550             
43551             if(p) {
43552                 for (var i = 0; i < p.length; i++) {
43553                     for (var j = 0; j < this.allCountries.length; j++) {
43554                         if(this.allCountries[j].iso2 == p[i]) {
43555                             var t = this.allCountries[j];
43556                             this.allCountries.splice(j,1);
43557                             this.allCountries.unshift(t);
43558                         }
43559                     } 
43560                 }
43561             }
43562             
43563             this.store.proxy.data = {
43564                 success: true,
43565                 data: this.allCountries
43566             };
43567             
43568             return cfg;
43569         },
43570         
43571         initEvents : function()
43572         {
43573             this.createList();
43574             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43575             
43576             this.indicator = this.indicatorEl();
43577             this.flag = this.flagEl();
43578             this.dialCodeHolder = this.dialCodeHolderEl();
43579             
43580             this.trigger = this.el.select('div.flag-box',true).first();
43581             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43582             
43583             var _this = this;
43584             
43585             (function(){
43586                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43587                 _this.list.setWidth(lw);
43588             }).defer(100);
43589             
43590             this.list.on('mouseover', this.onViewOver, this);
43591             this.list.on('mousemove', this.onViewMove, this);
43592             this.inputEl().on("keyup", this.onKeyUp, this);
43593             this.inputEl().on("keypress", this.onKeyPress, this);
43594             
43595             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43596
43597             this.view = new Roo.View(this.list, this.tpl, {
43598                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43599             });
43600             
43601             this.view.on('click', this.onViewClick, this);
43602             this.setValue(this.defaultDialCode);
43603         },
43604         
43605         onTriggerClick : function(e)
43606         {
43607             Roo.log('trigger click');
43608             if(this.disabled){
43609                 return;
43610             }
43611             
43612             if(this.isExpanded()){
43613                 this.collapse();
43614                 this.hasFocus = false;
43615             }else {
43616                 this.store.load({});
43617                 this.hasFocus = true;
43618                 this.expand();
43619             }
43620         },
43621         
43622         isExpanded : function()
43623         {
43624             return this.list.isVisible();
43625         },
43626         
43627         collapse : function()
43628         {
43629             if(!this.isExpanded()){
43630                 return;
43631             }
43632             this.list.hide();
43633             Roo.get(document).un('mousedown', this.collapseIf, this);
43634             Roo.get(document).un('mousewheel', this.collapseIf, this);
43635             this.fireEvent('collapse', this);
43636             this.validate();
43637         },
43638         
43639         expand : function()
43640         {
43641             Roo.log('expand');
43642
43643             if(this.isExpanded() || !this.hasFocus){
43644                 return;
43645             }
43646             
43647             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43648             this.list.setWidth(lw);
43649             
43650             this.list.show();
43651             this.restrictHeight();
43652             
43653             Roo.get(document).on('mousedown', this.collapseIf, this);
43654             Roo.get(document).on('mousewheel', this.collapseIf, this);
43655             
43656             this.fireEvent('expand', this);
43657         },
43658         
43659         restrictHeight : function()
43660         {
43661             this.list.alignTo(this.inputEl(), this.listAlign);
43662             this.list.alignTo(this.inputEl(), this.listAlign);
43663         },
43664         
43665         onViewOver : function(e, t)
43666         {
43667             if(this.inKeyMode){
43668                 return;
43669             }
43670             var item = this.view.findItemFromChild(t);
43671             
43672             if(item){
43673                 var index = this.view.indexOf(item);
43674                 this.select(index, false);
43675             }
43676         },
43677
43678         // private
43679         onViewClick : function(view, doFocus, el, e)
43680         {
43681             var index = this.view.getSelectedIndexes()[0];
43682             
43683             var r = this.store.getAt(index);
43684             
43685             if(r){
43686                 this.onSelect(r, index);
43687             }
43688             if(doFocus !== false && !this.blockFocus){
43689                 this.inputEl().focus();
43690             }
43691         },
43692         
43693         onViewMove : function(e, t)
43694         {
43695             this.inKeyMode = false;
43696         },
43697         
43698         select : function(index, scrollIntoView)
43699         {
43700             this.selectedIndex = index;
43701             this.view.select(index);
43702             if(scrollIntoView !== false){
43703                 var el = this.view.getNode(index);
43704                 if(el){
43705                     this.list.scrollChildIntoView(el, false);
43706                 }
43707             }
43708         },
43709         
43710         createList : function()
43711         {
43712             this.list = Roo.get(document.body).createChild({
43713                 tag: 'ul',
43714                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43715                 style: 'display:none'
43716             });
43717             
43718             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43719         },
43720         
43721         collapseIf : function(e)
43722         {
43723             var in_combo  = e.within(this.el);
43724             var in_list =  e.within(this.list);
43725             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43726             
43727             if (in_combo || in_list || is_list) {
43728                 return;
43729             }
43730             this.collapse();
43731         },
43732         
43733         onSelect : function(record, index)
43734         {
43735             if(this.fireEvent('beforeselect', this, record, index) !== false){
43736                 
43737                 this.setFlagClass(record.data.iso2);
43738                 this.setDialCode(record.data.dialCode);
43739                 this.hasFocus = false;
43740                 this.collapse();
43741                 this.fireEvent('select', this, record, index);
43742             }
43743         },
43744         
43745         flagEl : function()
43746         {
43747             var flag = this.el.select('div.flag',true).first();
43748             if(!flag){
43749                 return false;
43750             }
43751             return flag;
43752         },
43753         
43754         dialCodeHolderEl : function()
43755         {
43756             var d = this.el.select('input.dial-code-holder',true).first();
43757             if(!d){
43758                 return false;
43759             }
43760             return d;
43761         },
43762         
43763         setDialCode : function(v)
43764         {
43765             this.dialCodeHolder.dom.value = '+'+v;
43766         },
43767         
43768         setFlagClass : function(n)
43769         {
43770             this.flag.dom.className = 'flag '+n;
43771         },
43772         
43773         getValue : function()
43774         {
43775             var v = this.inputEl().getValue();
43776             if(this.dialCodeHolder) {
43777                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43778             }
43779             return v;
43780         },
43781         
43782         setValue : function(v)
43783         {
43784             var d = this.getDialCode(v);
43785             
43786             //invalid dial code
43787             if(v.length == 0 || !d || d.length == 0) {
43788                 if(this.rendered){
43789                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43790                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43791                 }
43792                 return;
43793             }
43794             
43795             //valid dial code
43796             this.setFlagClass(this.dialCodeMapping[d].iso2);
43797             this.setDialCode(d);
43798             this.inputEl().dom.value = v.replace('+'+d,'');
43799             this.hiddenEl().dom.value = this.getValue();
43800             
43801             this.validate();
43802         },
43803         
43804         getDialCode : function(v)
43805         {
43806             v = v ||  '';
43807             
43808             if (v.length == 0) {
43809                 return this.dialCodeHolder.dom.value;
43810             }
43811             
43812             var dialCode = "";
43813             if (v.charAt(0) != "+") {
43814                 return false;
43815             }
43816             var numericChars = "";
43817             for (var i = 1; i < v.length; i++) {
43818               var c = v.charAt(i);
43819               if (!isNaN(c)) {
43820                 numericChars += c;
43821                 if (this.dialCodeMapping[numericChars]) {
43822                   dialCode = v.substr(1, i);
43823                 }
43824                 if (numericChars.length == 4) {
43825                   break;
43826                 }
43827               }
43828             }
43829             return dialCode;
43830         },
43831         
43832         reset : function()
43833         {
43834             this.setValue(this.defaultDialCode);
43835             this.validate();
43836         },
43837         
43838         hiddenEl : function()
43839         {
43840             return this.el.select('input.hidden-tel-input',true).first();
43841         },
43842         
43843         // after setting val
43844         onKeyUp : function(e){
43845             this.setValue(this.getValue());
43846         },
43847         
43848         onKeyPress : function(e){
43849             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43850                 e.stopEvent();
43851             }
43852         }
43853         
43854 });
43855 /**
43856  * @class Roo.bootstrap.form.MoneyField
43857  * @extends Roo.bootstrap.form.ComboBox
43858  * Bootstrap MoneyField class
43859  * 
43860  * @constructor
43861  * Create a new MoneyField.
43862  * @param {Object} config Configuration options
43863  */
43864
43865 Roo.bootstrap.form.MoneyField = function(config) {
43866     
43867     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43868     
43869 };
43870
43871 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43872     
43873     /**
43874      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43875      */
43876     allowDecimals : true,
43877     /**
43878      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43879      */
43880     decimalSeparator : ".",
43881     /**
43882      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43883      */
43884     decimalPrecision : 0,
43885     /**
43886      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43887      */
43888     allowNegative : true,
43889     /**
43890      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43891      */
43892     allowZero: true,
43893     /**
43894      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43895      */
43896     minValue : Number.NEGATIVE_INFINITY,
43897     /**
43898      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43899      */
43900     maxValue : Number.MAX_VALUE,
43901     /**
43902      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43903      */
43904     minText : "The minimum value for this field is {0}",
43905     /**
43906      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43907      */
43908     maxText : "The maximum value for this field is {0}",
43909     /**
43910      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43911      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43912      */
43913     nanText : "{0} is not a valid number",
43914     /**
43915      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43916      */
43917     castInt : true,
43918     /**
43919      * @cfg {String} defaults currency of the MoneyField
43920      * value should be in lkey
43921      */
43922     defaultCurrency : false,
43923     /**
43924      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43925      */
43926     thousandsDelimiter : false,
43927     /**
43928      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43929      */
43930     max_length: false,
43931     
43932     inputlg : 9,
43933     inputmd : 9,
43934     inputsm : 9,
43935     inputxs : 6,
43936      /**
43937      * @cfg {Roo.data.Store} store  Store to lookup currency??
43938      */
43939     store : false,
43940     
43941     getAutoCreate : function()
43942     {
43943         var align = this.labelAlign || this.parentLabelAlign();
43944         
43945         var id = Roo.id();
43946
43947         var cfg = {
43948             cls: 'form-group',
43949             cn: []
43950         };
43951
43952         var input =  {
43953             tag: 'input',
43954             id : id,
43955             cls : 'form-control roo-money-amount-input',
43956             autocomplete: 'new-password'
43957         };
43958         
43959         var hiddenInput = {
43960             tag: 'input',
43961             type: 'hidden',
43962             id: Roo.id(),
43963             cls: 'hidden-number-input'
43964         };
43965         
43966         if(this.max_length) {
43967             input.maxlength = this.max_length; 
43968         }
43969         
43970         if (this.name) {
43971             hiddenInput.name = this.name;
43972         }
43973
43974         if (this.disabled) {
43975             input.disabled = true;
43976         }
43977
43978         var clg = 12 - this.inputlg;
43979         var cmd = 12 - this.inputmd;
43980         var csm = 12 - this.inputsm;
43981         var cxs = 12 - this.inputxs;
43982         
43983         var container = {
43984             tag : 'div',
43985             cls : 'row roo-money-field',
43986             cn : [
43987                 {
43988                     tag : 'div',
43989                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43990                     cn : [
43991                         {
43992                             tag : 'div',
43993                             cls: 'roo-select2-container input-group',
43994                             cn: [
43995                                 {
43996                                     tag : 'input',
43997                                     cls : 'form-control roo-money-currency-input',
43998                                     autocomplete: 'new-password',
43999                                     readOnly : 1,
44000                                     name : this.currencyName
44001                                 },
44002                                 {
44003                                     tag :'span',
44004                                     cls : 'input-group-addon',
44005                                     cn : [
44006                                         {
44007                                             tag: 'span',
44008                                             cls: 'caret'
44009                                         }
44010                                     ]
44011                                 }
44012                             ]
44013                         }
44014                     ]
44015                 },
44016                 {
44017                     tag : 'div',
44018                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44019                     cn : [
44020                         {
44021                             tag: 'div',
44022                             cls: this.hasFeedback ? 'has-feedback' : '',
44023                             cn: [
44024                                 input
44025                             ]
44026                         }
44027                     ]
44028                 }
44029             ]
44030             
44031         };
44032         
44033         if (this.fieldLabel.length) {
44034             var indicator = {
44035                 tag: 'i',
44036                 tooltip: 'This field is required'
44037             };
44038
44039             var label = {
44040                 tag: 'label',
44041                 'for':  id,
44042                 cls: 'control-label',
44043                 cn: []
44044             };
44045
44046             var label_text = {
44047                 tag: 'span',
44048                 html: this.fieldLabel
44049             };
44050
44051             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44052             label.cn = [
44053                 indicator,
44054                 label_text
44055             ];
44056
44057             if(this.indicatorpos == 'right') {
44058                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44059                 label.cn = [
44060                     label_text,
44061                     indicator
44062                 ];
44063             }
44064
44065             if(align == 'left') {
44066                 container = {
44067                     tag: 'div',
44068                     cn: [
44069                         container
44070                     ]
44071                 };
44072
44073                 if(this.labelWidth > 12){
44074                     label.style = "width: " + this.labelWidth + 'px';
44075                 }
44076                 if(this.labelWidth < 13 && this.labelmd == 0){
44077                     this.labelmd = this.labelWidth;
44078                 }
44079                 if(this.labellg > 0){
44080                     label.cls += ' col-lg-' + this.labellg;
44081                     input.cls += ' col-lg-' + (12 - this.labellg);
44082                 }
44083                 if(this.labelmd > 0){
44084                     label.cls += ' col-md-' + this.labelmd;
44085                     container.cls += ' col-md-' + (12 - this.labelmd);
44086                 }
44087                 if(this.labelsm > 0){
44088                     label.cls += ' col-sm-' + this.labelsm;
44089                     container.cls += ' col-sm-' + (12 - this.labelsm);
44090                 }
44091                 if(this.labelxs > 0){
44092                     label.cls += ' col-xs-' + this.labelxs;
44093                     container.cls += ' col-xs-' + (12 - this.labelxs);
44094                 }
44095             }
44096         }
44097
44098         cfg.cn = [
44099             label,
44100             container,
44101             hiddenInput
44102         ];
44103         
44104         var settings = this;
44105
44106         ['xs','sm','md','lg'].map(function(size){
44107             if (settings[size]) {
44108                 cfg.cls += ' col-' + size + '-' + settings[size];
44109             }
44110         });
44111         
44112         return cfg;
44113     },
44114     
44115     initEvents : function()
44116     {
44117         this.indicator = this.indicatorEl();
44118         
44119         this.initCurrencyEvent();
44120         
44121         this.initNumberEvent();
44122     },
44123     
44124     initCurrencyEvent : function()
44125     {
44126         if (!this.store) {
44127             throw "can not find store for combo";
44128         }
44129         
44130         this.store = Roo.factory(this.store, Roo.data);
44131         this.store.parent = this;
44132         
44133         this.createList();
44134         
44135         this.triggerEl = this.el.select('.input-group-addon', true).first();
44136         
44137         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44138         
44139         var _this = this;
44140         
44141         (function(){
44142             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44143             _this.list.setWidth(lw);
44144         }).defer(100);
44145         
44146         this.list.on('mouseover', this.onViewOver, this);
44147         this.list.on('mousemove', this.onViewMove, this);
44148         this.list.on('scroll', this.onViewScroll, this);
44149         
44150         if(!this.tpl){
44151             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44152         }
44153         
44154         this.view = new Roo.View(this.list, this.tpl, {
44155             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44156         });
44157         
44158         this.view.on('click', this.onViewClick, this);
44159         
44160         this.store.on('beforeload', this.onBeforeLoad, this);
44161         this.store.on('load', this.onLoad, this);
44162         this.store.on('loadexception', this.onLoadException, this);
44163         
44164         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44165             "up" : function(e){
44166                 this.inKeyMode = true;
44167                 this.selectPrev();
44168             },
44169
44170             "down" : function(e){
44171                 if(!this.isExpanded()){
44172                     this.onTriggerClick();
44173                 }else{
44174                     this.inKeyMode = true;
44175                     this.selectNext();
44176                 }
44177             },
44178
44179             "enter" : function(e){
44180                 this.collapse();
44181                 
44182                 if(this.fireEvent("specialkey", this, e)){
44183                     this.onViewClick(false);
44184                 }
44185                 
44186                 return true;
44187             },
44188
44189             "esc" : function(e){
44190                 this.collapse();
44191             },
44192
44193             "tab" : function(e){
44194                 this.collapse();
44195                 
44196                 if(this.fireEvent("specialkey", this, e)){
44197                     this.onViewClick(false);
44198                 }
44199                 
44200                 return true;
44201             },
44202
44203             scope : this,
44204
44205             doRelay : function(foo, bar, hname){
44206                 if(hname == 'down' || this.scope.isExpanded()){
44207                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44208                 }
44209                 return true;
44210             },
44211
44212             forceKeyDown: true
44213         });
44214         
44215         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44216         
44217     },
44218     
44219     initNumberEvent : function(e)
44220     {
44221         this.inputEl().on("keydown" , this.fireKey,  this);
44222         this.inputEl().on("focus", this.onFocus,  this);
44223         this.inputEl().on("blur", this.onBlur,  this);
44224         
44225         this.inputEl().relayEvent('keyup', this);
44226         
44227         if(this.indicator){
44228             this.indicator.addClass('invisible');
44229         }
44230  
44231         this.originalValue = this.getValue();
44232         
44233         if(this.validationEvent == 'keyup'){
44234             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44235             this.inputEl().on('keyup', this.filterValidation, this);
44236         }
44237         else if(this.validationEvent !== false){
44238             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44239         }
44240         
44241         if(this.selectOnFocus){
44242             this.on("focus", this.preFocus, this);
44243             
44244         }
44245         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44246             this.inputEl().on("keypress", this.filterKeys, this);
44247         } else {
44248             this.inputEl().relayEvent('keypress', this);
44249         }
44250         
44251         var allowed = "0123456789";
44252         
44253         if(this.allowDecimals){
44254             allowed += this.decimalSeparator;
44255         }
44256         
44257         if(this.allowNegative){
44258             allowed += "-";
44259         }
44260         
44261         if(this.thousandsDelimiter) {
44262             allowed += ",";
44263         }
44264         
44265         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44266         
44267         var keyPress = function(e){
44268             
44269             var k = e.getKey();
44270             
44271             var c = e.getCharCode();
44272             
44273             if(
44274                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44275                     allowed.indexOf(String.fromCharCode(c)) === -1
44276             ){
44277                 e.stopEvent();
44278                 return;
44279             }
44280             
44281             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44282                 return;
44283             }
44284             
44285             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44286                 e.stopEvent();
44287             }
44288         };
44289         
44290         this.inputEl().on("keypress", keyPress, this);
44291         
44292     },
44293     
44294     onTriggerClick : function(e)
44295     {   
44296         if(this.disabled){
44297             return;
44298         }
44299         
44300         this.page = 0;
44301         this.loadNext = false;
44302         
44303         if(this.isExpanded()){
44304             this.collapse();
44305             return;
44306         }
44307         
44308         this.hasFocus = true;
44309         
44310         if(this.triggerAction == 'all') {
44311             this.doQuery(this.allQuery, true);
44312             return;
44313         }
44314         
44315         this.doQuery(this.getRawValue());
44316     },
44317     
44318     getCurrency : function()
44319     {   
44320         var v = this.currencyEl().getValue();
44321         
44322         return v;
44323     },
44324     
44325     restrictHeight : function()
44326     {
44327         this.list.alignTo(this.currencyEl(), this.listAlign);
44328         this.list.alignTo(this.currencyEl(), this.listAlign);
44329     },
44330     
44331     onViewClick : function(view, doFocus, el, e)
44332     {
44333         var index = this.view.getSelectedIndexes()[0];
44334         
44335         var r = this.store.getAt(index);
44336         
44337         if(r){
44338             this.onSelect(r, index);
44339         }
44340     },
44341     
44342     onSelect : function(record, index){
44343         
44344         if(this.fireEvent('beforeselect', this, record, index) !== false){
44345         
44346             this.setFromCurrencyData(index > -1 ? record.data : false);
44347             
44348             this.collapse();
44349             
44350             this.fireEvent('select', this, record, index);
44351         }
44352     },
44353     
44354     setFromCurrencyData : function(o)
44355     {
44356         var currency = '';
44357         
44358         this.lastCurrency = o;
44359         
44360         if (this.currencyField) {
44361             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44362         } else {
44363             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44364         }
44365         
44366         this.lastSelectionText = currency;
44367         
44368         //setting default currency
44369         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44370             this.setCurrency(this.defaultCurrency);
44371             return;
44372         }
44373         
44374         this.setCurrency(currency);
44375     },
44376     
44377     setFromData : function(o)
44378     {
44379         var c = {};
44380         
44381         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44382         
44383         this.setFromCurrencyData(c);
44384         
44385         var value = '';
44386         
44387         if (this.name) {
44388             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44389         } else {
44390             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44391         }
44392         
44393         this.setValue(value);
44394         
44395     },
44396     
44397     setCurrency : function(v)
44398     {   
44399         this.currencyValue = v;
44400         
44401         if(this.rendered){
44402             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44403             this.validate();
44404         }
44405     },
44406     
44407     setValue : function(v)
44408     {
44409         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44410         
44411         this.value = v;
44412         
44413         if(this.rendered){
44414             
44415             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44416             
44417             this.inputEl().dom.value = (v == '') ? '' :
44418                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44419             
44420             if(!this.allowZero && v === '0') {
44421                 this.hiddenEl().dom.value = '';
44422                 this.inputEl().dom.value = '';
44423             }
44424             
44425             this.validate();
44426         }
44427     },
44428     
44429     getRawValue : function()
44430     {
44431         var v = this.inputEl().getValue();
44432         
44433         return v;
44434     },
44435     
44436     getValue : function()
44437     {
44438         return this.fixPrecision(this.parseValue(this.getRawValue()));
44439     },
44440     
44441     parseValue : function(value)
44442     {
44443         if(this.thousandsDelimiter) {
44444             value += "";
44445             r = new RegExp(",", "g");
44446             value = value.replace(r, "");
44447         }
44448         
44449         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44450         return isNaN(value) ? '' : value;
44451         
44452     },
44453     
44454     fixPrecision : function(value)
44455     {
44456         if(this.thousandsDelimiter) {
44457             value += "";
44458             r = new RegExp(",", "g");
44459             value = value.replace(r, "");
44460         }
44461         
44462         var nan = isNaN(value);
44463         
44464         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44465             return nan ? '' : value;
44466         }
44467         return parseFloat(value).toFixed(this.decimalPrecision);
44468     },
44469     
44470     decimalPrecisionFcn : function(v)
44471     {
44472         return Math.floor(v);
44473     },
44474     
44475     validateValue : function(value)
44476     {
44477         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44478             return false;
44479         }
44480         
44481         var num = this.parseValue(value);
44482         
44483         if(isNaN(num)){
44484             this.markInvalid(String.format(this.nanText, value));
44485             return false;
44486         }
44487         
44488         if(num < this.minValue){
44489             this.markInvalid(String.format(this.minText, this.minValue));
44490             return false;
44491         }
44492         
44493         if(num > this.maxValue){
44494             this.markInvalid(String.format(this.maxText, this.maxValue));
44495             return false;
44496         }
44497         
44498         return true;
44499     },
44500     
44501     validate : function()
44502     {
44503         if(this.disabled || this.allowBlank){
44504             this.markValid();
44505             return true;
44506         }
44507         
44508         var currency = this.getCurrency();
44509         
44510         if(this.validateValue(this.getRawValue()) && currency.length){
44511             this.markValid();
44512             return true;
44513         }
44514         
44515         this.markInvalid();
44516         return false;
44517     },
44518     
44519     getName: function()
44520     {
44521         return this.name;
44522     },
44523     
44524     beforeBlur : function()
44525     {
44526         if(!this.castInt){
44527             return;
44528         }
44529         
44530         var v = this.parseValue(this.getRawValue());
44531         
44532         if(v || v == 0){
44533             this.setValue(v);
44534         }
44535     },
44536     
44537     onBlur : function()
44538     {
44539         this.beforeBlur();
44540         
44541         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44542             //this.el.removeClass(this.focusClass);
44543         }
44544         
44545         this.hasFocus = false;
44546         
44547         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44548             this.validate();
44549         }
44550         
44551         var v = this.getValue();
44552         
44553         if(String(v) !== String(this.startValue)){
44554             this.fireEvent('change', this, v, this.startValue);
44555         }
44556         
44557         this.fireEvent("blur", this);
44558     },
44559     
44560     inputEl : function()
44561     {
44562         return this.el.select('.roo-money-amount-input', true).first();
44563     },
44564     
44565     currencyEl : function()
44566     {
44567         return this.el.select('.roo-money-currency-input', true).first();
44568     },
44569     
44570     hiddenEl : function()
44571     {
44572         return this.el.select('input.hidden-number-input',true).first();
44573     }
44574     
44575 });/**
44576  * @class Roo.bootstrap.BezierSignature
44577  * @extends Roo.bootstrap.Component
44578  * Bootstrap BezierSignature class
44579  * This script refer to:
44580  *    Title: Signature Pad
44581  *    Author: szimek
44582  *    Availability: https://github.com/szimek/signature_pad
44583  *
44584  * @constructor
44585  * Create a new BezierSignature
44586  * @param {Object} config The config object
44587  */
44588
44589 Roo.bootstrap.BezierSignature = function(config){
44590     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44591     this.addEvents({
44592         "resize" : true
44593     });
44594 };
44595
44596 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44597 {
44598      
44599     curve_data: [],
44600     
44601     is_empty: true,
44602     
44603     mouse_btn_down: true,
44604     
44605     /**
44606      * @cfg {int} canvas height
44607      */
44608     canvas_height: '200px',
44609     
44610     /**
44611      * @cfg {float|function} Radius of a single dot.
44612      */ 
44613     dot_size: false,
44614     
44615     /**
44616      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44617      */
44618     min_width: 0.5,
44619     
44620     /**
44621      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44622      */
44623     max_width: 2.5,
44624     
44625     /**
44626      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44627      */
44628     throttle: 16,
44629     
44630     /**
44631      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44632      */
44633     min_distance: 5,
44634     
44635     /**
44636      * @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.
44637      */
44638     bg_color: 'rgba(0, 0, 0, 0)',
44639     
44640     /**
44641      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44642      */
44643     dot_color: 'black',
44644     
44645     /**
44646      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44647      */ 
44648     velocity_filter_weight: 0.7,
44649     
44650     /**
44651      * @cfg {function} Callback when stroke begin. 
44652      */
44653     onBegin: false,
44654     
44655     /**
44656      * @cfg {function} Callback when stroke end.
44657      */
44658     onEnd: false,
44659     
44660     getAutoCreate : function()
44661     {
44662         var cls = 'roo-signature column';
44663         
44664         if(this.cls){
44665             cls += ' ' + this.cls;
44666         }
44667         
44668         var col_sizes = [
44669             'lg',
44670             'md',
44671             'sm',
44672             'xs'
44673         ];
44674         
44675         for(var i = 0; i < col_sizes.length; i++) {
44676             if(this[col_sizes[i]]) {
44677                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44678             }
44679         }
44680         
44681         var cfg = {
44682             tag: 'div',
44683             cls: cls,
44684             cn: [
44685                 {
44686                     tag: 'div',
44687                     cls: 'roo-signature-body',
44688                     cn: [
44689                         {
44690                             tag: 'canvas',
44691                             cls: 'roo-signature-body-canvas',
44692                             height: this.canvas_height,
44693                             width: this.canvas_width
44694                         }
44695                     ]
44696                 },
44697                 {
44698                     tag: 'input',
44699                     type: 'file',
44700                     style: 'display: none'
44701                 }
44702             ]
44703         };
44704         
44705         return cfg;
44706     },
44707     
44708     initEvents: function() 
44709     {
44710         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44711         
44712         var canvas = this.canvasEl();
44713         
44714         // mouse && touch event swapping...
44715         canvas.dom.style.touchAction = 'none';
44716         canvas.dom.style.msTouchAction = 'none';
44717         
44718         this.mouse_btn_down = false;
44719         canvas.on('mousedown', this._handleMouseDown, this);
44720         canvas.on('mousemove', this._handleMouseMove, this);
44721         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44722         
44723         if (window.PointerEvent) {
44724             canvas.on('pointerdown', this._handleMouseDown, this);
44725             canvas.on('pointermove', this._handleMouseMove, this);
44726             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44727         }
44728         
44729         if ('ontouchstart' in window) {
44730             canvas.on('touchstart', this._handleTouchStart, this);
44731             canvas.on('touchmove', this._handleTouchMove, this);
44732             canvas.on('touchend', this._handleTouchEnd, this);
44733         }
44734         
44735         Roo.EventManager.onWindowResize(this.resize, this, true);
44736         
44737         // file input event
44738         this.fileEl().on('change', this.uploadImage, this);
44739         
44740         this.clear();
44741         
44742         this.resize();
44743     },
44744     
44745     resize: function(){
44746         
44747         var canvas = this.canvasEl().dom;
44748         var ctx = this.canvasElCtx();
44749         var img_data = false;
44750         
44751         if(canvas.width > 0) {
44752             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44753         }
44754         // setting canvas width will clean img data
44755         canvas.width = 0;
44756         
44757         var style = window.getComputedStyle ? 
44758             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44759             
44760         var padding_left = parseInt(style.paddingLeft) || 0;
44761         var padding_right = parseInt(style.paddingRight) || 0;
44762         
44763         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44764         
44765         if(img_data) {
44766             ctx.putImageData(img_data, 0, 0);
44767         }
44768     },
44769     
44770     _handleMouseDown: function(e)
44771     {
44772         if (e.browserEvent.which === 1) {
44773             this.mouse_btn_down = true;
44774             this.strokeBegin(e);
44775         }
44776     },
44777     
44778     _handleMouseMove: function (e)
44779     {
44780         if (this.mouse_btn_down) {
44781             this.strokeMoveUpdate(e);
44782         }
44783     },
44784     
44785     _handleMouseUp: function (e)
44786     {
44787         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44788             this.mouse_btn_down = false;
44789             this.strokeEnd(e);
44790         }
44791     },
44792     
44793     _handleTouchStart: function (e) {
44794         
44795         e.preventDefault();
44796         if (e.browserEvent.targetTouches.length === 1) {
44797             // var touch = e.browserEvent.changedTouches[0];
44798             // this.strokeBegin(touch);
44799             
44800              this.strokeBegin(e); // assume e catching the correct xy...
44801         }
44802     },
44803     
44804     _handleTouchMove: function (e) {
44805         e.preventDefault();
44806         // var touch = event.targetTouches[0];
44807         // _this._strokeMoveUpdate(touch);
44808         this.strokeMoveUpdate(e);
44809     },
44810     
44811     _handleTouchEnd: function (e) {
44812         var wasCanvasTouched = e.target === this.canvasEl().dom;
44813         if (wasCanvasTouched) {
44814             e.preventDefault();
44815             // var touch = event.changedTouches[0];
44816             // _this._strokeEnd(touch);
44817             this.strokeEnd(e);
44818         }
44819     },
44820     
44821     reset: function () {
44822         this._lastPoints = [];
44823         this._lastVelocity = 0;
44824         this._lastWidth = (this.min_width + this.max_width) / 2;
44825         this.canvasElCtx().fillStyle = this.dot_color;
44826     },
44827     
44828     strokeMoveUpdate: function(e)
44829     {
44830         this.strokeUpdate(e);
44831         
44832         if (this.throttle) {
44833             this.throttleStroke(this.strokeUpdate, this.throttle);
44834         }
44835         else {
44836             this.strokeUpdate(e);
44837         }
44838     },
44839     
44840     strokeBegin: function(e)
44841     {
44842         var newPointGroup = {
44843             color: this.dot_color,
44844             points: []
44845         };
44846         
44847         if (typeof this.onBegin === 'function') {
44848             this.onBegin(e);
44849         }
44850         
44851         this.curve_data.push(newPointGroup);
44852         this.reset();
44853         this.strokeUpdate(e);
44854     },
44855     
44856     strokeUpdate: function(e)
44857     {
44858         var rect = this.canvasEl().dom.getBoundingClientRect();
44859         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44860         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44861         var lastPoints = lastPointGroup.points;
44862         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44863         var isLastPointTooClose = lastPoint
44864             ? point.distanceTo(lastPoint) <= this.min_distance
44865             : false;
44866         var color = lastPointGroup.color;
44867         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44868             var curve = this.addPoint(point);
44869             if (!lastPoint) {
44870                 this.drawDot({color: color, point: point});
44871             }
44872             else if (curve) {
44873                 this.drawCurve({color: color, curve: curve});
44874             }
44875             lastPoints.push({
44876                 time: point.time,
44877                 x: point.x,
44878                 y: point.y
44879             });
44880         }
44881     },
44882     
44883     strokeEnd: function(e)
44884     {
44885         this.strokeUpdate(e);
44886         if (typeof this.onEnd === 'function') {
44887             this.onEnd(e);
44888         }
44889     },
44890     
44891     addPoint:  function (point) {
44892         var _lastPoints = this._lastPoints;
44893         _lastPoints.push(point);
44894         if (_lastPoints.length > 2) {
44895             if (_lastPoints.length === 3) {
44896                 _lastPoints.unshift(_lastPoints[0]);
44897             }
44898             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44899             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44900             _lastPoints.shift();
44901             return curve;
44902         }
44903         return null;
44904     },
44905     
44906     calculateCurveWidths: function (startPoint, endPoint) {
44907         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44908             (1 - this.velocity_filter_weight) * this._lastVelocity;
44909
44910         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44911         var widths = {
44912             end: newWidth,
44913             start: this._lastWidth
44914         };
44915         
44916         this._lastVelocity = velocity;
44917         this._lastWidth = newWidth;
44918         return widths;
44919     },
44920     
44921     drawDot: function (_a) {
44922         var color = _a.color, point = _a.point;
44923         var ctx = this.canvasElCtx();
44924         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44925         ctx.beginPath();
44926         this.drawCurveSegment(point.x, point.y, width);
44927         ctx.closePath();
44928         ctx.fillStyle = color;
44929         ctx.fill();
44930     },
44931     
44932     drawCurve: function (_a) {
44933         var color = _a.color, curve = _a.curve;
44934         var ctx = this.canvasElCtx();
44935         var widthDelta = curve.endWidth - curve.startWidth;
44936         var drawSteps = Math.floor(curve.length()) * 2;
44937         ctx.beginPath();
44938         ctx.fillStyle = color;
44939         for (var i = 0; i < drawSteps; i += 1) {
44940         var t = i / drawSteps;
44941         var tt = t * t;
44942         var ttt = tt * t;
44943         var u = 1 - t;
44944         var uu = u * u;
44945         var uuu = uu * u;
44946         var x = uuu * curve.startPoint.x;
44947         x += 3 * uu * t * curve.control1.x;
44948         x += 3 * u * tt * curve.control2.x;
44949         x += ttt * curve.endPoint.x;
44950         var y = uuu * curve.startPoint.y;
44951         y += 3 * uu * t * curve.control1.y;
44952         y += 3 * u * tt * curve.control2.y;
44953         y += ttt * curve.endPoint.y;
44954         var width = curve.startWidth + ttt * widthDelta;
44955         this.drawCurveSegment(x, y, width);
44956         }
44957         ctx.closePath();
44958         ctx.fill();
44959     },
44960     
44961     drawCurveSegment: function (x, y, width) {
44962         var ctx = this.canvasElCtx();
44963         ctx.moveTo(x, y);
44964         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44965         this.is_empty = false;
44966     },
44967     
44968     clear: function()
44969     {
44970         var ctx = this.canvasElCtx();
44971         var canvas = this.canvasEl().dom;
44972         ctx.fillStyle = this.bg_color;
44973         ctx.clearRect(0, 0, canvas.width, canvas.height);
44974         ctx.fillRect(0, 0, canvas.width, canvas.height);
44975         this.curve_data = [];
44976         this.reset();
44977         this.is_empty = true;
44978     },
44979     
44980     fileEl: function()
44981     {
44982         return  this.el.select('input',true).first();
44983     },
44984     
44985     canvasEl: function()
44986     {
44987         return this.el.select('canvas',true).first();
44988     },
44989     
44990     canvasElCtx: function()
44991     {
44992         return this.el.select('canvas',true).first().dom.getContext('2d');
44993     },
44994     
44995     getImage: function(type)
44996     {
44997         if(this.is_empty) {
44998             return false;
44999         }
45000         
45001         // encryption ?
45002         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45003     },
45004     
45005     drawFromImage: function(img_src)
45006     {
45007         var img = new Image();
45008         
45009         img.onload = function(){
45010             this.canvasElCtx().drawImage(img, 0, 0);
45011         }.bind(this);
45012         
45013         img.src = img_src;
45014         
45015         this.is_empty = false;
45016     },
45017     
45018     selectImage: function()
45019     {
45020         this.fileEl().dom.click();
45021     },
45022     
45023     uploadImage: function(e)
45024     {
45025         var reader = new FileReader();
45026         
45027         reader.onload = function(e){
45028             var img = new Image();
45029             img.onload = function(){
45030                 this.reset();
45031                 this.canvasElCtx().drawImage(img, 0, 0);
45032             }.bind(this);
45033             img.src = e.target.result;
45034         }.bind(this);
45035         
45036         reader.readAsDataURL(e.target.files[0]);
45037     },
45038     
45039     // Bezier Point Constructor
45040     Point: (function () {
45041         function Point(x, y, time) {
45042             this.x = x;
45043             this.y = y;
45044             this.time = time || Date.now();
45045         }
45046         Point.prototype.distanceTo = function (start) {
45047             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45048         };
45049         Point.prototype.equals = function (other) {
45050             return this.x === other.x && this.y === other.y && this.time === other.time;
45051         };
45052         Point.prototype.velocityFrom = function (start) {
45053             return this.time !== start.time
45054             ? this.distanceTo(start) / (this.time - start.time)
45055             : 0;
45056         };
45057         return Point;
45058     }()),
45059     
45060     
45061     // Bezier Constructor
45062     Bezier: (function () {
45063         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45064             this.startPoint = startPoint;
45065             this.control2 = control2;
45066             this.control1 = control1;
45067             this.endPoint = endPoint;
45068             this.startWidth = startWidth;
45069             this.endWidth = endWidth;
45070         }
45071         Bezier.fromPoints = function (points, widths, scope) {
45072             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45073             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45074             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45075         };
45076         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45077             var dx1 = s1.x - s2.x;
45078             var dy1 = s1.y - s2.y;
45079             var dx2 = s2.x - s3.x;
45080             var dy2 = s2.y - s3.y;
45081             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45082             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45083             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45084             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45085             var dxm = m1.x - m2.x;
45086             var dym = m1.y - m2.y;
45087             var k = l2 / (l1 + l2);
45088             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45089             var tx = s2.x - cm.x;
45090             var ty = s2.y - cm.y;
45091             return {
45092                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45093                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45094             };
45095         };
45096         Bezier.prototype.length = function () {
45097             var steps = 10;
45098             var length = 0;
45099             var px;
45100             var py;
45101             for (var i = 0; i <= steps; i += 1) {
45102                 var t = i / steps;
45103                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45104                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45105                 if (i > 0) {
45106                     var xdiff = cx - px;
45107                     var ydiff = cy - py;
45108                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45109                 }
45110                 px = cx;
45111                 py = cy;
45112             }
45113             return length;
45114         };
45115         Bezier.prototype.point = function (t, start, c1, c2, end) {
45116             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45117             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45118             + (3.0 * c2 * (1.0 - t) * t * t)
45119             + (end * t * t * t);
45120         };
45121         return Bezier;
45122     }()),
45123     
45124     throttleStroke: function(fn, wait) {
45125       if (wait === void 0) { wait = 250; }
45126       var previous = 0;
45127       var timeout = null;
45128       var result;
45129       var storedContext;
45130       var storedArgs;
45131       var later = function () {
45132           previous = Date.now();
45133           timeout = null;
45134           result = fn.apply(storedContext, storedArgs);
45135           if (!timeout) {
45136               storedContext = null;
45137               storedArgs = [];
45138           }
45139       };
45140       return function wrapper() {
45141           var args = [];
45142           for (var _i = 0; _i < arguments.length; _i++) {
45143               args[_i] = arguments[_i];
45144           }
45145           var now = Date.now();
45146           var remaining = wait - (now - previous);
45147           storedContext = this;
45148           storedArgs = args;
45149           if (remaining <= 0 || remaining > wait) {
45150               if (timeout) {
45151                   clearTimeout(timeout);
45152                   timeout = null;
45153               }
45154               previous = now;
45155               result = fn.apply(storedContext, storedArgs);
45156               if (!timeout) {
45157                   storedContext = null;
45158                   storedArgs = [];
45159               }
45160           }
45161           else if (!timeout) {
45162               timeout = window.setTimeout(later, remaining);
45163           }
45164           return result;
45165       };
45166   }
45167   
45168 });
45169
45170  
45171
45172  // old names for form elements
45173 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45174 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45175 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45176 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45177 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45178 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45179 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45180 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45181 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45182 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45183 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45184 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45185 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45186 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45187 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45188 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45189 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45190 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45191 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45192 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45193 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45194 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45195 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45196 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45197 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45198 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45199
45200 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45201 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45202
45203 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45204 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45205
45206 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45207 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45208 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45209 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45210