fix button # triggering url change
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
19
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
21  * Based on:
22  * Ext JS Library 1.1.1
23  * Copyright(c) 2006-2007, Ext JS, LLC.
24  *
25  * Originally Released Under LGPL - original licence link has changed is not relivant.
26  *
27  * Fork - LGPL
28  * <script type="text/javascript">
29  */
30
31
32 /**
33  * @class Roo.Shadow
34  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
35  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
36  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
37  * @constructor
38  * Create a new Shadow
39  * @param {Object} config The config object
40  */
41 Roo.Shadow = function(config){
42     Roo.apply(this, config);
43     if(typeof this.mode != "string"){
44         this.mode = this.defaultMode;
45     }
46     var o = this.offset, a = {h: 0};
47     var rad = Math.floor(this.offset/2);
48     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
49         case "drop":
50             a.w = 0;
51             a.l = a.t = o;
52             a.t -= 1;
53             if(Roo.isIE){
54                 a.l -= this.offset + rad;
55                 a.t -= this.offset + rad;
56                 a.w -= rad;
57                 a.h -= rad;
58                 a.t += 1;
59             }
60         break;
61         case "sides":
62             a.w = (o*2);
63             a.l = -o;
64             a.t = o-1;
65             if(Roo.isIE){
66                 a.l -= (this.offset - rad);
67                 a.t -= this.offset + rad;
68                 a.l += 1;
69                 a.w -= (this.offset - rad)*2;
70                 a.w -= rad + 1;
71                 a.h -= 1;
72             }
73         break;
74         case "frame":
75             a.w = a.h = (o*2);
76             a.l = a.t = -o;
77             a.t += 1;
78             a.h -= 2;
79             if(Roo.isIE){
80                 a.l -= (this.offset - rad);
81                 a.t -= (this.offset - rad);
82                 a.l += 1;
83                 a.w -= (this.offset + rad + 1);
84                 a.h -= (this.offset + rad);
85                 a.h += 1;
86             }
87         break;
88     };
89
90     this.adjusts = a;
91 };
92
93 Roo.Shadow.prototype = {
94     /**
95      * @cfg {String} mode
96      * The shadow display mode.  Supports the following options:<br />
97      * sides: Shadow displays on both sides and bottom only<br />
98      * frame: Shadow displays equally on all four sides<br />
99      * drop: Traditional bottom-right drop shadow (default)
100      */
101     mode: false,
102     /**
103      * @cfg {String} offset
104      * The number of pixels to offset the shadow from the element (defaults to 4)
105      */
106     offset: 4,
107
108     // private
109     defaultMode: "drop",
110
111     /**
112      * Displays the shadow under the target element
113      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
114      */
115     show : function(target){
116         target = Roo.get(target);
117         if(!this.el){
118             this.el = Roo.Shadow.Pool.pull();
119             if(this.el.dom.nextSibling != target.dom){
120                 this.el.insertBefore(target);
121             }
122         }
123         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
124         if(Roo.isIE){
125             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
126         }
127         this.realign(
128             target.getLeft(true),
129             target.getTop(true),
130             target.getWidth(),
131             target.getHeight()
132         );
133         this.el.dom.style.display = "block";
134     },
135
136     /**
137      * Returns true if the shadow is visible, else false
138      */
139     isVisible : function(){
140         return this.el ? true : false;  
141     },
142
143     /**
144      * Direct alignment when values are already available. Show must be called at least once before
145      * calling this method to ensure it is initialized.
146      * @param {Number} left The target element left position
147      * @param {Number} top The target element top position
148      * @param {Number} width The target element width
149      * @param {Number} height The target element height
150      */
151     realign : function(l, t, w, h){
152         if(!this.el){
153             return;
154         }
155         var a = this.adjusts, d = this.el.dom, s = d.style;
156         var iea = 0;
157         s.left = (l+a.l)+"px";
158         s.top = (t+a.t)+"px";
159         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
160  
161         if(s.width != sws || s.height != shs){
162             s.width = sws;
163             s.height = shs;
164             if(!Roo.isIE){
165                 var cn = d.childNodes;
166                 var sww = Math.max(0, (sw-12))+"px";
167                 cn[0].childNodes[1].style.width = sww;
168                 cn[1].childNodes[1].style.width = sww;
169                 cn[2].childNodes[1].style.width = sww;
170                 cn[1].style.height = Math.max(0, (sh-12))+"px";
171             }
172         }
173     },
174
175     /**
176      * Hides this shadow
177      */
178     hide : function(){
179         if(this.el){
180             this.el.dom.style.display = "none";
181             Roo.Shadow.Pool.push(this.el);
182             delete this.el;
183         }
184     },
185
186     /**
187      * Adjust the z-index of this shadow
188      * @param {Number} zindex The new z-index
189      */
190     setZIndex : function(z){
191         this.zIndex = z;
192         if(this.el){
193             this.el.setStyle("z-index", z);
194         }
195     }
196 };
197
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
200     var p = [];
201     var markup = Roo.isIE ?
202                  '<div class="x-ie-shadow"></div>' :
203                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
204     return {
205         pull : function(){
206             var sh = p.shift();
207             if(!sh){
208                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209                 sh.autoBoxAdjust = false;
210             }
211             return sh;
212         },
213
214         push : function(sh){
215             p.push(sh);
216         }
217     };
218 }();/*
219  * - LGPL
220  *
221  * base class for bootstrap elements.
222  * 
223  */
224
225 Roo.bootstrap = Roo.bootstrap || {};
226 /**
227  * @class Roo.bootstrap.Component
228  * @extends Roo.Component
229  * @abstract
230  * @children Roo.bootstrap.Component
231  * Bootstrap Component base class
232  * @cfg {String} cls css class
233  * @cfg {String} style any extra css
234  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
236  * @cfg {string} dataId cutomer id
237  * @cfg {string} name Specifies name attribute
238  * @cfg {string} tooltip  Text for the tooltip
239  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
240  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
241  
242  * @constructor
243  * Do not use directly - it does not do anything..
244  * @param {Object} config The config object
245  */
246
247
248
249 Roo.bootstrap.Component = function(config){
250     Roo.bootstrap.Component.superclass.constructor.call(this, config);
251        
252     this.addEvents({
253         /**
254          * @event childrenrendered
255          * Fires when the children have been rendered..
256          * @param {Roo.bootstrap.Component} this
257          */
258         "childrenrendered" : true
259         
260         
261         
262     });
263     
264     
265 };
266
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
268     
269     
270     allowDomMove : false, // to stop relocations in parent onRender...
271     
272     cls : false,
273     
274     style : false,
275     
276     autoCreate : false,
277     
278     tooltip : null,
279     /**
280      * Initialize Events for the element
281      */
282     initEvents : function() { },
283     
284     xattr : false,
285     
286     parentId : false,
287     
288     can_build_overlaid : true,
289     
290     container_method : false,
291     
292     dataId : false,
293     
294     name : false,
295     
296     parent: function() {
297         // returns the parent component..
298         return Roo.ComponentMgr.get(this.parentId)
299         
300         
301     },
302     
303     // private
304     onRender : function(ct, position)
305     {
306        // Roo.log("Call onRender: " + this.xtype);
307         
308         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
309         
310         if(this.el){
311             if (this.el.attr('xtype')) {
312                 this.el.attr('xtypex', this.el.attr('xtype'));
313                 this.el.dom.removeAttribute('xtype');
314                 
315                 this.initEvents();
316             }
317             
318             return;
319         }
320         
321          
322         
323         var cfg = Roo.apply({},  this.getAutoCreate());
324         
325         cfg.id = this.id || Roo.id();
326         
327         // fill in the extra attributes 
328         if (this.xattr && typeof(this.xattr) =='object') {
329             for (var i in this.xattr) {
330                 cfg[i] = this.xattr[i];
331             }
332         }
333         
334         if(this.dataId){
335             cfg.dataId = this.dataId;
336         }
337         
338         if (this.cls) {
339             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
340         }
341         
342         if (this.style) { // fixme needs to support more complex style data.
343             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
344         }
345         
346         if(this.name){
347             cfg.name = this.name;
348         }
349         
350         this.el = ct.createChild(cfg, position);
351         
352         if (this.tooltip) {
353             this.tooltipEl().attr('tooltip', this.tooltip);
354         }
355         
356         if(this.tabIndex !== undefined){
357             this.el.dom.setAttribute('tabIndex', this.tabIndex);
358         }
359         
360         this.initEvents();
361         
362     },
363     /**
364      * Fetch the element to add children to
365      * @return {Roo.Element} defaults to this.el
366      */
367     getChildContainer : function()
368     {
369         return this.el;
370     },
371     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
372     {
373         return Roo.get(document.body);
374     },
375     
376     /**
377      * Fetch the element to display the tooltip on.
378      * @return {Roo.Element} defaults to this.el
379      */
380     tooltipEl : function()
381     {
382         return this.el;
383     },
384         
385     addxtype  : function(tree,cntr)
386     {
387         var cn = this;
388         
389         cn = Roo.factory(tree);
390         //Roo.log(['addxtype', cn]);
391            
392         cn.parentType = this.xtype; //??
393         cn.parentId = this.id;
394         
395         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396         if (typeof(cn.container_method) == 'string') {
397             cntr = cn.container_method;
398         }
399         
400         
401         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
402         
403         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
404         
405         var build_from_html =  Roo.XComponent.build_from_html;
406           
407         var is_body  = (tree.xtype == 'Body') ;
408           
409         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
410           
411         var self_cntr_el = Roo.get(this[cntr](false));
412         
413         // do not try and build conditional elements 
414         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
415             return false;
416         }
417         
418         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420                 return this.addxtypeChild(tree,cntr, is_body);
421             }
422             
423             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
424                 
425             if(echild){
426                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
427             }
428             
429             Roo.log('skipping render');
430             return cn;
431             
432         }
433         
434         var ret = false;
435         if (!build_from_html) {
436             return false;
437         }
438         
439         // this i think handles overlaying multiple children of the same type
440         // with the sam eelement.. - which might be buggy..
441         while (true) {
442             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
443             
444             if (!echild) {
445                 break;
446             }
447             
448             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
449                 break;
450             }
451             
452             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453         }
454        
455         return ret;
456     },
457     
458     
459     addxtypeChild : function (tree, cntr, is_body)
460     {
461         Roo.debug && Roo.log('addxtypeChild:' + cntr);
462         var cn = this;
463         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
464         
465         
466         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467                     (typeof(tree['flexy:foreach']) != 'undefined');
468           
469     
470         
471         skip_children = false;
472         // render the element if it's not BODY.
473         if (!is_body) {
474             
475             // if parent was disabled, then do not try and create the children..
476             if(!this[cntr](true)){
477                 tree.items = [];
478                 return tree;
479             }
480            
481             cn = Roo.factory(tree);
482            
483             cn.parentType = this.xtype; //??
484             cn.parentId = this.id;
485             
486             var build_from_html =  Roo.XComponent.build_from_html;
487             
488             
489             // does the container contain child eleemnts with 'xtype' attributes.
490             // that match this xtype..
491             // note - when we render we create these as well..
492             // so we should check to see if body has xtype set.
493             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
494                
495                 var self_cntr_el = Roo.get(this[cntr](false));
496                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
497                 if (echild) { 
498                     //Roo.log(Roo.XComponent.build_from_html);
499                     //Roo.log("got echild:");
500                     //Roo.log(echild);
501                 }
502                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503                 // and are not displayed -this causes this to use up the wrong element when matching.
504                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
505                 
506                 
507                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
509                   
510                   
511                   
512                     cn.el = echild;
513                   //  Roo.log("GOT");
514                     //echild.dom.removeAttribute('xtype');
515                 } else {
516                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517                     Roo.debug && Roo.log(self_cntr_el);
518                     Roo.debug && Roo.log(echild);
519                     Roo.debug && Roo.log(cn);
520                 }
521             }
522            
523             
524            
525             // if object has flexy:if - then it may or may not be rendered.
526             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
527                 // skip a flexy if element.
528                 Roo.debug && Roo.log('skipping render');
529                 Roo.debug && Roo.log(tree);
530                 if (!cn.el) {
531                     Roo.debug && Roo.log('skipping all children');
532                     skip_children = true;
533                 }
534                 
535              } else {
536                  
537                 // actually if flexy:foreach is found, we really want to create 
538                 // multiple copies here...
539                 //Roo.log('render');
540                 //Roo.log(this[cntr]());
541                 // some elements do not have render methods.. like the layouts...
542                 /*
543                 if(this[cntr](true) === false){
544                     cn.items = [];
545                     return cn;
546                 }
547                 */
548                 cn.render && cn.render(this[cntr](true));
549                 
550              }
551             // then add the element..
552         }
553          
554         // handle the kids..
555         
556         var nitems = [];
557         /*
558         if (typeof (tree.menu) != 'undefined') {
559             tree.menu.parentType = cn.xtype;
560             tree.menu.triggerEl = cn.el;
561             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
562             
563         }
564         */
565         if (!tree.items || !tree.items.length) {
566             cn.items = nitems;
567             //Roo.log(["no children", this]);
568             
569             return cn;
570         }
571          
572         var items = tree.items;
573         delete tree.items;
574         
575         //Roo.log(items.length);
576             // add the items..
577         if (!skip_children) {    
578             for(var i =0;i < items.length;i++) {
579               //  Roo.log(['add child', items[i]]);
580                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
581             }
582         }
583         
584         cn.items = nitems;
585         
586         //Roo.log("fire childrenrendered");
587         
588         cn.fireEvent('childrenrendered', this);
589         
590         return cn;
591     },
592     
593     /**
594      * Set the element that will be used to show or hide
595      */
596     setVisibilityEl : function(el)
597     {
598         this.visibilityEl = el;
599     },
600     
601      /**
602      * Get the element that will be used to show or hide
603      */
604     getVisibilityEl : function()
605     {
606         if (typeof(this.visibilityEl) == 'object') {
607             return this.visibilityEl;
608         }
609         
610         if (typeof(this.visibilityEl) == 'string') {
611             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612         }
613         
614         return this.getEl();
615     },
616     
617     /**
618      * Show a component - removes 'hidden' class
619      */
620     show : function()
621     {
622         if(!this.getVisibilityEl()){
623             return;
624         }
625          
626         this.getVisibilityEl().removeClass(['hidden','d-none']);
627         
628         this.fireEvent('show', this);
629         
630         
631     },
632     /**
633      * Hide a component - adds 'hidden' class
634      */
635     hide: function()
636     {
637         if(!this.getVisibilityEl()){
638             return;
639         }
640         
641         this.getVisibilityEl().addClass(['hidden','d-none']);
642         
643         this.fireEvent('hide', this);
644         
645     }
646 });
647
648  /*
649  * - LGPL
650  *
651  * element
652  * 
653  */
654
655 /**
656  * @class Roo.bootstrap.Element
657  * @extends Roo.bootstrap.Component
658  * @children Roo.bootstrap.Component
659  * Bootstrap Element class (basically a DIV used to make random stuff )
660  * 
661  * @cfg {String} html contents of the element
662  * @cfg {String} tag tag of the element
663  * @cfg {String} cls class of the element
664  * @cfg {Boolean} preventDefault (true|false) default false
665  * @cfg {Boolean} clickable (true|false) default false
666  * @cfg {String} role default blank - set to button to force cursor pointer
667  
668  * 
669  * @constructor
670  * Create a new Element
671  * @param {Object} config The config object
672  */
673
674 Roo.bootstrap.Element = function(config){
675     Roo.bootstrap.Element.superclass.constructor.call(this, config);
676     
677     this.addEvents({
678         // raw events
679         /**
680          * @event click
681          * When a element is chick
682          * @param {Roo.bootstrap.Element} this
683          * @param {Roo.EventObject} e
684          */
685         "click" : true 
686         
687       
688     });
689 };
690
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
692     
693     tag: 'div',
694     cls: '',
695     html: '',
696     preventDefault: false, 
697     clickable: false,
698     tapedTwice : false,
699     role : false,
700     
701     getAutoCreate : function(){
702         
703         var cfg = {
704             tag: this.tag,
705             // cls: this.cls, double assign in parent class Component.js :: onRender
706             html: this.html
707         };
708         if (this.role !== false) {
709             cfg.role = this.role;
710         }
711         
712         return cfg;
713     },
714     
715     initEvents: function() 
716     {
717         Roo.bootstrap.Element.superclass.initEvents.call(this);
718         
719         if(this.clickable){
720             this.el.on('click', this.onClick, this);
721         }
722         
723         
724     },
725     
726     onClick : function(e)
727     {
728         if(this.preventDefault){
729             e.preventDefault();
730         }
731         
732         this.fireEvent('click', this, e); // why was this double click before?
733     },
734     
735     
736     
737
738     
739     
740     getValue : function()
741     {
742         return this.el.dom.innerHTML;
743     },
744     
745     setValue : function(value)
746     {
747         this.el.dom.innerHTML = value;
748     }
749    
750 });
751
752  
753
754  /*
755  * - LGPL
756  *
757  * dropable area
758  * 
759  */
760
761 /**
762  * @class Roo.bootstrap.DropTarget
763  * @extends Roo.bootstrap.Element
764  * Bootstrap DropTarget class
765  
766  * @cfg {string} name dropable name
767  * 
768  * @constructor
769  * Create a new Dropable Area
770  * @param {Object} config The config object
771  */
772
773 Roo.bootstrap.DropTarget = function(config){
774     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
775     
776     this.addEvents({
777         // raw events
778         /**
779          * @event click
780          * When a element is chick
781          * @param {Roo.bootstrap.Element} this
782          * @param {Roo.EventObject} e
783          */
784         "drop" : true
785     });
786 };
787
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
789     
790     
791     getAutoCreate : function(){
792         
793          
794     },
795     
796     initEvents: function() 
797     {
798         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
800             ddGroup: this.name,
801             listeners : {
802                 drop : this.dragDrop.createDelegate(this),
803                 enter : this.dragEnter.createDelegate(this),
804                 out : this.dragOut.createDelegate(this),
805                 over : this.dragOver.createDelegate(this)
806             }
807             
808         });
809         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
810     },
811     
812     dragDrop : function(source,e,data)
813     {
814         // user has to decide how to impliment this.
815         Roo.log('drop');
816         Roo.log(this);
817         //this.fireEvent('drop', this, source, e ,data);
818         return false;
819     },
820     
821     dragEnter : function(n, dd, e, data)
822     {
823         // probably want to resize the element to match the dropped element..
824         Roo.log("enter");
825         this.originalSize = this.el.getSize();
826         this.el.setSize( n.el.getSize());
827         this.dropZone.DDM.refreshCache(this.name);
828         Roo.log([n, dd, e, data]);
829     },
830     
831     dragOut : function(value)
832     {
833         // resize back to normal
834         Roo.log("out");
835         this.el.setSize(this.originalSize);
836         this.dropZone.resetConstraints();
837     },
838     
839     dragOver : function()
840     {
841         // ??? do nothing?
842     }
843    
844 });
845
846  
847
848  /*
849  * - LGPL
850  *
851  * Body
852  *
853  */
854
855 /**
856  * @class Roo.bootstrap.Body
857  * @extends Roo.bootstrap.Component
858  * @children Roo.bootstrap.Component 
859  * @parent none builder
860  * Bootstrap Body class
861  *
862  * @constructor
863  * Create a new body
864  * @param {Object} config The config object
865  */
866
867 Roo.bootstrap.Body = function(config){
868
869     config = config || {};
870
871     Roo.bootstrap.Body.superclass.constructor.call(this, config);
872     this.el = Roo.get(config.el ? config.el : document.body );
873     if (this.cls && this.cls.length) {
874         Roo.get(document.body).addClass(this.cls);
875     }
876 };
877
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
879
880     is_body : true,// just to make sure it's constructed?
881
882         autoCreate : {
883         cls: 'container'
884     },
885     onRender : function(ct, position)
886     {
887        /* Roo.log("Roo.bootstrap.Body - onRender");
888         if (this.cls && this.cls.length) {
889             Roo.get(document.body).addClass(this.cls);
890         }
891         // style??? xttr???
892         */
893     }
894
895
896
897
898 });
899 /*
900  * - LGPL
901  *
902  * button group
903  * 
904  */
905
906
907 /**
908  * @class Roo.bootstrap.ButtonGroup
909  * @extends Roo.bootstrap.Component
910  * Bootstrap ButtonGroup class
911  * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
912  * 
913  * @cfg {String} size lg | sm | xs (default empty normal)
914  * @cfg {String} align vertical | justified  (default none)
915  * @cfg {String} direction up | down (default down)
916  * @cfg {Boolean} toolbar false | true
917  * @cfg {Boolean} btn true | false
918  * 
919  * 
920  * @constructor
921  * Create a new Input
922  * @param {Object} config The config object
923  */
924
925 Roo.bootstrap.ButtonGroup = function(config){
926     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
927 };
928
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
930     
931     size: '',
932     align: '',
933     direction: '',
934     toolbar: false,
935     btn: true,
936
937     getAutoCreate : function(){
938         var cfg = {
939             cls: 'btn-group',
940             html : null
941         };
942         
943         cfg.html = this.html || cfg.html;
944         
945         if (this.toolbar) {
946             cfg = {
947                 cls: 'btn-toolbar',
948                 html: null
949             };
950             
951             return cfg;
952         }
953         
954         if (['vertical','justified'].indexOf(this.align)!==-1) {
955             cfg.cls = 'btn-group-' + this.align;
956             
957             if (this.align == 'justified') {
958                 console.log(this.items);
959             }
960         }
961         
962         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963             cfg.cls += ' btn-group-' + this.size;
964         }
965         
966         if (this.direction == 'up') {
967             cfg.cls += ' dropup' ;
968         }
969         
970         return cfg;
971     },
972     /**
973      * Add a button to the group (similar to NavItem API.)
974      */
975     addItem : function(cfg)
976     {
977         var cn = new Roo.bootstrap.Button(cfg);
978         //this.register(cn);
979         cn.parentId = this.id;
980         cn.onRender(this.el, null);
981         return cn;
982     }
983    
984 });
985
986  /*
987  * - LGPL
988  *
989  * button
990  * 
991  */
992
993 /**
994  * @class Roo.bootstrap.Button
995  * @extends Roo.bootstrap.Component
996  * Bootstrap Button class
997  * @cfg {String} html The button content
998  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001  * @cfg {String} size (lg|sm|xs)
1002  * @cfg {String} tag (a|input|submit)
1003  * @cfg {String} href empty or href
1004  * @cfg {Boolean} disabled default false;
1005  * @cfg {Boolean} isClose default false;
1006  * @cfg {String} glyphicon depricated - use fa
1007  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008  * @cfg {String} badge text for badge
1009  * @cfg {String} theme (default|glow)  
1010  * @cfg {Boolean} inverse dark themed version
1011  * @cfg {Boolean} toggle is it a slidy toggle button
1012  * @cfg {Boolean} pressed   default null - if the button ahs active state
1013  * @cfg {String} ontext text for on slidy toggle state
1014  * @cfg {String} offtext text for off slidy toggle state
1015  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1016  * @cfg {Boolean} removeClass remove the standard class..
1017  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1018  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
1020
1021  * @constructor
1022  * Create a new button
1023  * @param {Object} config The config object
1024  */
1025
1026
1027 Roo.bootstrap.Button = function(config){
1028     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1029     
1030     this.addEvents({
1031         // raw events
1032         /**
1033          * @event click
1034          * When a button is pressed
1035          * @param {Roo.bootstrap.Button} btn
1036          * @param {Roo.EventObject} e
1037          */
1038         "click" : true,
1039         /**
1040          * @event dblclick
1041          * When a button is double clicked
1042          * @param {Roo.bootstrap.Button} btn
1043          * @param {Roo.EventObject} e
1044          */
1045         "dblclick" : true,
1046          /**
1047          * @event toggle
1048          * After the button has been toggles
1049          * @param {Roo.bootstrap.Button} btn
1050          * @param {Roo.EventObject} e
1051          * @param {boolean} pressed (also available as button.pressed)
1052          */
1053         "toggle" : true
1054     });
1055 };
1056
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1058     html: false,
1059     active: false,
1060     weight: '',
1061     badge_weight: '',
1062     outline : false,
1063     size: '',
1064     tag: 'button',
1065     href: '',
1066     disabled: false,
1067     isClose: false,
1068     glyphicon: '',
1069     fa: '',
1070     badge: '',
1071     theme: 'default',
1072     inverse: false,
1073     
1074     toggle: false,
1075     ontext: 'ON',
1076     offtext: 'OFF',
1077     defaulton: true,
1078     preventDefault: true,
1079     removeClass: false,
1080     name: false,
1081     target: false,
1082     group : false,
1083      
1084     pressed : null,
1085      
1086     
1087     getAutoCreate : function(){
1088         
1089         var cfg = {
1090             tag : 'button',
1091             cls : 'roo-button',
1092             html: ''
1093         };
1094         
1095         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097             this.tag = 'button';
1098         } else {
1099             cfg.tag = this.tag;
1100         }
1101         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1102         
1103         if (this.toggle == true) {
1104             cfg={
1105                 tag: 'div',
1106                 cls: 'slider-frame roo-button',
1107                 cn: [
1108                     {
1109                         tag: 'span',
1110                         'data-on-text':'ON',
1111                         'data-off-text':'OFF',
1112                         cls: 'slider-button',
1113                         html: this.offtext
1114                     }
1115                 ]
1116             };
1117             // why are we validating the weights?
1118             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119                 cfg.cls +=  ' ' + this.weight;
1120             }
1121             
1122             return cfg;
1123         }
1124         
1125         if (this.isClose) {
1126             cfg.cls += ' close';
1127             
1128             cfg["aria-hidden"] = true;
1129             
1130             cfg.html = "&times;";
1131             
1132             return cfg;
1133         }
1134              
1135         
1136         if (this.theme==='default') {
1137             cfg.cls = 'btn roo-button';
1138             
1139             //if (this.parentType != 'Navbar') {
1140             this.weight = this.weight.length ?  this.weight : 'default';
1141             //}
1142             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1143                 
1144                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146                 cfg.cls += ' btn-' + outline + weight;
1147                 if (this.weight == 'default') {
1148                     // BC
1149                     cfg.cls += ' btn-' + this.weight;
1150                 }
1151             }
1152         } else if (this.theme==='glow') {
1153             
1154             cfg.tag = 'a';
1155             cfg.cls = 'btn-glow roo-button';
1156             
1157             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1158                 
1159                 cfg.cls += ' ' + this.weight;
1160             }
1161         }
1162    
1163         
1164         if (this.inverse) {
1165             this.cls += ' inverse';
1166         }
1167         
1168         
1169         if (this.active || this.pressed === true) {
1170             cfg.cls += ' active';
1171         }
1172         
1173         if (this.disabled) {
1174             cfg.disabled = 'disabled';
1175         }
1176         
1177         if (this.items) {
1178             Roo.log('changing to ul' );
1179             cfg.tag = 'ul';
1180             this.glyphicon = 'caret';
1181             if (Roo.bootstrap.version == 4) {
1182                 this.fa = 'caret-down';
1183             }
1184             
1185         }
1186         
1187         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1188          
1189         //gsRoo.log(this.parentType);
1190         if (this.parentType === 'Navbar' && !this.parent().bar) {
1191             Roo.log('changing to li?');
1192             
1193             cfg.tag = 'li';
1194             
1195             cfg.cls = '';
1196             cfg.cn =  [{
1197                 tag : 'a',
1198                 cls : 'roo-button',
1199                 html : this.html,
1200                 href : this.href || '#'
1201             }];
1202             if (this.menu) {
1203                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1204                 cfg.cls += ' dropdown';
1205             }   
1206             
1207             delete cfg.html;
1208             
1209         }
1210         
1211        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1212         
1213         if (this.glyphicon) {
1214             cfg.html = ' ' + cfg.html;
1215             
1216             cfg.cn = [
1217                 {
1218                     tag: 'span',
1219                     cls: 'glyphicon glyphicon-' + this.glyphicon
1220                 }
1221             ];
1222         }
1223         if (this.fa) {
1224             cfg.html = ' ' + cfg.html;
1225             
1226             cfg.cn = [
1227                 {
1228                     tag: 'i',
1229                     cls: 'fa fas fa-' + this.fa
1230                 }
1231             ];
1232         }
1233         
1234         if (this.badge) {
1235             cfg.html += ' ';
1236             
1237             cfg.tag = 'a';
1238             
1239 //            cfg.cls='btn roo-button';
1240             
1241             cfg.href=this.href;
1242             
1243             var value = cfg.html;
1244             
1245             if(this.glyphicon){
1246                 value = {
1247                     tag: 'span',
1248                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1249                     html: this.html
1250                 };
1251             }
1252             if(this.fa){
1253                 value = {
1254                     tag: 'i',
1255                     cls: 'fa fas fa-' + this.fa,
1256                     html: this.html
1257                 };
1258             }
1259             
1260             var bw = this.badge_weight.length ? this.badge_weight :
1261                 (this.weight.length ? this.weight : 'secondary');
1262             bw = bw == 'default' ? 'secondary' : bw;
1263             
1264             cfg.cn = [
1265                 value,
1266                 {
1267                     tag: 'span',
1268                     cls: 'badge badge-' + bw,
1269                     html: this.badge
1270                 }
1271             ];
1272             
1273             cfg.html='';
1274         }
1275         
1276         if (this.menu) {
1277             cfg.cls += ' dropdown';
1278             cfg.html = typeof(cfg.html) != 'undefined' ?
1279                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1280         }
1281         
1282         if (cfg.tag !== 'a' && this.href !== '') {
1283             throw "Tag must be a to set href.";
1284         } else if (this.href.length > 0) {
1285             cfg.href = this.href;
1286         }
1287         
1288         if(this.removeClass){
1289             cfg.cls = '';
1290         }
1291         
1292         if(this.target){
1293             cfg.target = this.target;
1294         }
1295         
1296         return cfg;
1297     },
1298     initEvents: function() {
1299        // Roo.log('init events?');
1300 //        Roo.log(this.el.dom);
1301         // add the menu...
1302         
1303         if (typeof (this.menu) != 'undefined') {
1304             this.menu.parentType = this.xtype;
1305             this.menu.triggerEl = this.el;
1306             this.addxtype(Roo.apply({}, this.menu));
1307         }
1308
1309
1310         if (this.el.hasClass('roo-button')) {
1311              this.el.on('click', this.onClick, this);
1312              this.el.on('dblclick', this.onDblClick, this);
1313         } else {
1314              this.el.select('.roo-button').on('click', this.onClick, this);
1315              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1316              
1317         }
1318         // why?
1319         if(this.removeClass){
1320             this.el.on('click', this.onClick, this);
1321         }
1322         
1323         if (this.group === true) {
1324              if (this.pressed === false || this.pressed === true) {
1325                 // nothing
1326             } else {
1327                 this.pressed = false;
1328                 this.setActive(this.pressed);
1329             }
1330             
1331         }
1332         
1333         this.el.enableDisplayMode();
1334         
1335     },
1336     onClick : function(e)
1337     {
1338         if (this.disabled) {
1339             return;
1340         }
1341         
1342         Roo.log('button on click ');
1343         if(this.href === '' || this.preventDefault){
1344             e.preventDefault();
1345         }
1346         
1347         if (this.group) {
1348             if (this.pressed) {
1349                 // do nothing -
1350                 return;
1351             }
1352             this.setActive(true);
1353             var pi = this.parent().items;
1354             for (var i = 0;i < pi.length;i++) {
1355                 if (this == pi[i]) {
1356                     continue;
1357                 }
1358                 if (pi[i].el.hasClass('roo-button')) {
1359                     pi[i].setActive(false);
1360                 }
1361             }
1362             this.fireEvent('click', this, e);            
1363             return;
1364         }
1365         
1366         if (this.pressed === true || this.pressed === false) {
1367             this.toggleActive(e);
1368         }
1369         
1370         
1371         this.fireEvent('click', this, e);
1372     },
1373     onDblClick: function(e)
1374     {
1375         if (this.disabled) {
1376             return;
1377         }
1378         if(this.preventDefault){
1379             e.preventDefault();
1380         }
1381         this.fireEvent('dblclick', this, e);
1382     },
1383     /**
1384      * Enables this button
1385      */
1386     enable : function()
1387     {
1388         this.disabled = false;
1389         this.el.removeClass('disabled');
1390         this.el.dom.removeAttribute("disabled");
1391     },
1392     
1393     /**
1394      * Disable this button
1395      */
1396     disable : function()
1397     {
1398         this.disabled = true;
1399         this.el.addClass('disabled');
1400         this.el.attr("disabled", "disabled")
1401     },
1402      /**
1403      * sets the active state on/off, 
1404      * @param {Boolean} state (optional) Force a particular state
1405      */
1406     setActive : function(v) {
1407         
1408         this.el[v ? 'addClass' : 'removeClass']('active');
1409         this.pressed = v;
1410     },
1411      /**
1412      * toggles the current active state 
1413      */
1414     toggleActive : function(e)
1415     {
1416         this.setActive(!this.pressed); // this modifies pressed...
1417         this.fireEvent('toggle', this, e, this.pressed);
1418     },
1419      /**
1420      * get the current active state
1421      * @return {boolean} true if it's active
1422      */
1423     isActive : function()
1424     {
1425         return this.el.hasClass('active');
1426     },
1427     /**
1428      * set the text of the first selected button
1429      */
1430     setText : function(str)
1431     {
1432         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1433     },
1434     /**
1435      * get the text of the first selected button
1436      */
1437     getText : function()
1438     {
1439         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1440     },
1441     
1442     setWeight : function(str)
1443     {
1444         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1446         this.weight = str;
1447         var outline = this.outline ? 'outline-' : '';
1448         if (str == 'default') {
1449             this.el.addClass('btn-default btn-outline-secondary');        
1450             return;
1451         }
1452         this.el.addClass('btn-' + outline + str);        
1453     }
1454     
1455     
1456 });
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1458
1459 Roo.bootstrap.Button.weights = [
1460     'default',
1461     'secondary' ,
1462     'primary',
1463     'success',
1464     'info',
1465     'warning',
1466     'danger',
1467     'link',
1468     'light',
1469     'dark'              
1470    
1471 ];/*
1472  * - LGPL
1473  *
1474  * column
1475  * 
1476  */
1477
1478 /**
1479  * @class Roo.bootstrap.Column
1480  * @extends Roo.bootstrap.Component
1481  * @children Roo.bootstrap.Component
1482  * Bootstrap Column class
1483  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1491  *
1492  * 
1493  * @cfg {Boolean} hidden (true|false) hide the element
1494  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495  * @cfg {String} fa (ban|check|...) font awesome icon
1496  * @cfg {Number} fasize (1|2|....) font awsome size
1497
1498  * @cfg {String} icon (info-sign|check|...) glyphicon name
1499
1500  * @cfg {String} html content of column.
1501  * 
1502  * @constructor
1503  * Create a new Column
1504  * @param {Object} config The config object
1505  */
1506
1507 Roo.bootstrap.Column = function(config){
1508     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1509 };
1510
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1512     
1513     xs: false,
1514     sm: false,
1515     md: false,
1516     lg: false,
1517     xsoff: false,
1518     smoff: false,
1519     mdoff: false,
1520     lgoff: false,
1521     html: '',
1522     offset: 0,
1523     alert: false,
1524     fa: false,
1525     icon : false,
1526     hidden : false,
1527     fasize : 1,
1528     
1529     getAutoCreate : function(){
1530         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1531         
1532         cfg = {
1533             tag: 'div',
1534             cls: 'column'
1535         };
1536         
1537         var settings=this;
1538         var sizes =   ['xs','sm','md','lg'];
1539         sizes.map(function(size ,ix){
1540             //Roo.log( size + ':' + settings[size]);
1541             
1542             if (settings[size+'off'] !== false) {
1543                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1544             }
1545             
1546             if (settings[size] === false) {
1547                 return;
1548             }
1549             
1550             if (!settings[size]) { // 0 = hidden
1551                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1552                 // bootsrap4
1553                 for (var i = ix; i > -1; i--) {
1554                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1555                 }
1556                 
1557                 
1558                 return;
1559             }
1560             cfg.cls += ' col-' + size + '-' + settings[size] + (
1561                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1562             );
1563             
1564         });
1565         
1566         if (this.hidden) {
1567             cfg.cls += ' hidden';
1568         }
1569         
1570         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571             cfg.cls +=' alert alert-' + this.alert;
1572         }
1573         
1574         
1575         if (this.html.length) {
1576             cfg.html = this.html;
1577         }
1578         if (this.fa) {
1579             var fasize = '';
1580             if (this.fasize > 1) {
1581                 fasize = ' fa-' + this.fasize + 'x';
1582             }
1583             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1584             
1585             
1586         }
1587         if (this.icon) {
1588             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1589         }
1590         
1591         return cfg;
1592     }
1593    
1594 });
1595
1596  
1597
1598  /*
1599  * - LGPL
1600  *
1601  * page container.
1602  * 
1603  */
1604
1605
1606 /**
1607  * @class Roo.bootstrap.Container
1608  * @extends Roo.bootstrap.Component
1609  * @children Roo.bootstrap.Component
1610  * @parent builder
1611  * Bootstrap Container class
1612  * @cfg {Boolean} jumbotron is it a jumbotron element
1613  * @cfg {String} html content of element
1614  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1616  * @cfg {String} header content of header (for panel)
1617  * @cfg {String} footer content of footer (for panel)
1618  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619  * @cfg {String} tag (header|aside|section) type of HTML tag.
1620  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621  * @cfg {String} fa font awesome icon
1622  * @cfg {String} icon (info-sign|check|...) glyphicon name
1623  * @cfg {Boolean} hidden (true|false) hide the element
1624  * @cfg {Boolean} expandable (true|false) default false
1625  * @cfg {Boolean} expanded (true|false) default true
1626  * @cfg {String} rheader contet on the right of header
1627  * @cfg {Boolean} clickable (true|false) default false
1628
1629  *     
1630  * @constructor
1631  * Create a new Container
1632  * @param {Object} config The config object
1633  */
1634
1635 Roo.bootstrap.Container = function(config){
1636     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1637     
1638     this.addEvents({
1639         // raw events
1640          /**
1641          * @event expand
1642          * After the panel has been expand
1643          * 
1644          * @param {Roo.bootstrap.Container} this
1645          */
1646         "expand" : true,
1647         /**
1648          * @event collapse
1649          * After the panel has been collapsed
1650          * 
1651          * @param {Roo.bootstrap.Container} this
1652          */
1653         "collapse" : true,
1654         /**
1655          * @event click
1656          * When a element is chick
1657          * @param {Roo.bootstrap.Container} this
1658          * @param {Roo.EventObject} e
1659          */
1660         "click" : true
1661     });
1662 };
1663
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1665     
1666     jumbotron : false,
1667     well: '',
1668     panel : '',
1669     header: '',
1670     footer : '',
1671     sticky: '',
1672     tag : false,
1673     alert : false,
1674     fa: false,
1675     icon : false,
1676     expandable : false,
1677     rheader : '',
1678     expanded : true,
1679     clickable: false,
1680   
1681      
1682     getChildContainer : function() {
1683         
1684         if(!this.el){
1685             return false;
1686         }
1687         
1688         if (this.panel.length) {
1689             return this.el.select('.panel-body',true).first();
1690         }
1691         
1692         return this.el;
1693     },
1694     
1695     
1696     getAutoCreate : function(){
1697         
1698         var cfg = {
1699             tag : this.tag || 'div',
1700             html : '',
1701             cls : ''
1702         };
1703         if (this.jumbotron) {
1704             cfg.cls = 'jumbotron';
1705         }
1706         
1707         
1708         
1709         // - this is applied by the parent..
1710         //if (this.cls) {
1711         //    cfg.cls = this.cls + '';
1712         //}
1713         
1714         if (this.sticky.length) {
1715             
1716             var bd = Roo.get(document.body);
1717             if (!bd.hasClass('bootstrap-sticky')) {
1718                 bd.addClass('bootstrap-sticky');
1719                 Roo.select('html',true).setStyle('height', '100%');
1720             }
1721              
1722             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1723         }
1724         
1725         
1726         if (this.well.length) {
1727             switch (this.well) {
1728                 case 'lg':
1729                 case 'sm':
1730                     cfg.cls +=' well well-' +this.well;
1731                     break;
1732                 default:
1733                     cfg.cls +=' well';
1734                     break;
1735             }
1736         }
1737         
1738         if (this.hidden) {
1739             cfg.cls += ' hidden';
1740         }
1741         
1742         
1743         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744             cfg.cls +=' alert alert-' + this.alert;
1745         }
1746         
1747         var body = cfg;
1748         
1749         if (this.panel.length) {
1750             cfg.cls += ' panel panel-' + this.panel;
1751             cfg.cn = [];
1752             if (this.header.length) {
1753                 
1754                 var h = [];
1755                 
1756                 if(this.expandable){
1757                     
1758                     cfg.cls = cfg.cls + ' expandable';
1759                     
1760                     h.push({
1761                         tag: 'i',
1762                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1763                     });
1764                     
1765                 }
1766                 
1767                 h.push(
1768                     {
1769                         tag: 'span',
1770                         cls : 'panel-title',
1771                         html : (this.expandable ? '&nbsp;' : '') + this.header
1772                     },
1773                     {
1774                         tag: 'span',
1775                         cls: 'panel-header-right',
1776                         html: this.rheader
1777                     }
1778                 );
1779                 
1780                 cfg.cn.push({
1781                     cls : 'panel-heading',
1782                     style : this.expandable ? 'cursor: pointer' : '',
1783                     cn : h
1784                 });
1785                 
1786             }
1787             
1788             body = false;
1789             cfg.cn.push({
1790                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1791                 html : this.html
1792             });
1793             
1794             
1795             if (this.footer.length) {
1796                 cfg.cn.push({
1797                     cls : 'panel-footer',
1798                     html : this.footer
1799                     
1800                 });
1801             }
1802             
1803         }
1804         
1805         if (body) {
1806             body.html = this.html || cfg.html;
1807             // prefix with the icons..
1808             if (this.fa) {
1809                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1810             }
1811             if (this.icon) {
1812                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1813             }
1814             
1815             
1816         }
1817         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818             cfg.cls =  'container';
1819         }
1820         
1821         return cfg;
1822     },
1823     
1824     initEvents: function() 
1825     {
1826         if(this.expandable){
1827             var headerEl = this.headerEl();
1828         
1829             if(headerEl){
1830                 headerEl.on('click', this.onToggleClick, this);
1831             }
1832         }
1833         
1834         if(this.clickable){
1835             this.el.on('click', this.onClick, this);
1836         }
1837         
1838     },
1839     
1840     onToggleClick : function()
1841     {
1842         var headerEl = this.headerEl();
1843         
1844         if(!headerEl){
1845             return;
1846         }
1847         
1848         if(this.expanded){
1849             this.collapse();
1850             return;
1851         }
1852         
1853         this.expand();
1854     },
1855     
1856     expand : function()
1857     {
1858         if(this.fireEvent('expand', this)) {
1859             
1860             this.expanded = true;
1861             
1862             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1863             
1864             this.el.select('.panel-body',true).first().removeClass('hide');
1865             
1866             var toggleEl = this.toggleEl();
1867
1868             if(!toggleEl){
1869                 return;
1870             }
1871
1872             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1873         }
1874         
1875     },
1876     
1877     collapse : function()
1878     {
1879         if(this.fireEvent('collapse', this)) {
1880             
1881             this.expanded = false;
1882             
1883             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884             this.el.select('.panel-body',true).first().addClass('hide');
1885         
1886             var toggleEl = this.toggleEl();
1887
1888             if(!toggleEl){
1889                 return;
1890             }
1891
1892             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1893         }
1894     },
1895     
1896     toggleEl : function()
1897     {
1898         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1899             return;
1900         }
1901         
1902         return this.el.select('.panel-heading .fa',true).first();
1903     },
1904     
1905     headerEl : function()
1906     {
1907         if(!this.el || !this.panel.length || !this.header.length){
1908             return;
1909         }
1910         
1911         return this.el.select('.panel-heading',true).first()
1912     },
1913     
1914     bodyEl : function()
1915     {
1916         if(!this.el || !this.panel.length){
1917             return;
1918         }
1919         
1920         return this.el.select('.panel-body',true).first()
1921     },
1922     
1923     titleEl : function()
1924     {
1925         if(!this.el || !this.panel.length || !this.header.length){
1926             return;
1927         }
1928         
1929         return this.el.select('.panel-title',true).first();
1930     },
1931     
1932     setTitle : function(v)
1933     {
1934         var titleEl = this.titleEl();
1935         
1936         if(!titleEl){
1937             return;
1938         }
1939         
1940         titleEl.dom.innerHTML = v;
1941     },
1942     
1943     getTitle : function()
1944     {
1945         
1946         var titleEl = this.titleEl();
1947         
1948         if(!titleEl){
1949             return '';
1950         }
1951         
1952         return titleEl.dom.innerHTML;
1953     },
1954     
1955     setRightTitle : function(v)
1956     {
1957         var t = this.el.select('.panel-header-right',true).first();
1958         
1959         if(!t){
1960             return;
1961         }
1962         
1963         t.dom.innerHTML = v;
1964     },
1965     
1966     onClick : function(e)
1967     {
1968         e.preventDefault();
1969         
1970         this.fireEvent('click', this, e);
1971     }
1972 });
1973
1974  /**
1975  * @class Roo.bootstrap.Card
1976  * @extends Roo.bootstrap.Component
1977  * @children Roo.bootstrap.Component
1978  * @licence LGPL
1979  * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1980  *
1981  *
1982  * possible... may not be implemented..
1983  * @cfg {String} header_image  src url of image.
1984  * @cfg {String|Object} header
1985  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1987  * 
1988  * @cfg {String} title
1989  * @cfg {String} subtitle
1990  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991  * @cfg {String} footer
1992  
1993  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1994  * 
1995  * @cfg {String} margin (0|1|2|3|4|5|auto)
1996  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2002  *
2003  * @cfg {String} padding (0|1|2|3|4|5)
2004  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005  * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006  * @cfg {String} padding_left (0|1|2|3|4|5)
2007  * @cfg {String} padding_right (0|1|2|3|4|5)
2008  * @cfg {String} padding_x (0|1|2|3|4|5)
2009  * @cfg {String} padding_y (0|1|2|3|4|5)
2010  *
2011  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016  
2017  * @config {Boolean} dragable  if this card can be dragged.
2018  * @config {String} drag_group  group for drag
2019  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2020  * @config {String} drop_group  group for drag
2021  * 
2022  * @config {Boolean} collapsable can the body be collapsed.
2023  * @config {Boolean} collapsed is the body collapsed when rendered...
2024  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025  * @config {Boolean} rotated is the body rotated when rendered...
2026  * 
2027  * @constructor
2028  * Create a new Container
2029  * @param {Object} config The config object
2030  */
2031
2032 Roo.bootstrap.Card = function(config){
2033     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2034     
2035     this.addEvents({
2036          // raw events
2037         /**
2038          * @event drop
2039          * When a element a card is dropped
2040          * @param {Roo.bootstrap.Card} this
2041          *
2042          * 
2043          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044          * @param {String} position 'above' or 'below'
2045          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2046         
2047          */
2048         'drop' : true,
2049          /**
2050          * @event rotate
2051          * When a element a card is rotate
2052          * @param {Roo.bootstrap.Card} this
2053          * @param {Roo.Element} n the node being dropped?
2054          * @param {Boolean} rotate status
2055          */
2056         'rotate' : true,
2057         /**
2058          * @event cardover
2059          * When a card element is dragged over ready to drop (return false to block dropable)
2060          * @param {Roo.bootstrap.Card} this
2061          * @param {Object} data from dragdrop 
2062          */
2063          'cardover' : true
2064          
2065     });
2066 };
2067
2068
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2070     
2071     
2072     weight : '',
2073     
2074     margin: '', /// may be better in component?
2075     margin_top: '', 
2076     margin_bottom: '', 
2077     margin_left: '',
2078     margin_right: '',
2079     margin_x: '',
2080     margin_y: '',
2081     
2082     padding : '',
2083     padding_top: '', 
2084     padding_bottom: '', 
2085     padding_left: '',
2086     padding_right: '',
2087     padding_x: '',
2088     padding_y: '',
2089     
2090     display: '', 
2091     display_xs: '', 
2092     display_sm: '', 
2093     display_lg: '',
2094     display_xl: '',
2095  
2096     header_image  : '',
2097     header : '',
2098     header_size : 0,
2099     title : '',
2100     subtitle : '',
2101     html : '',
2102     footer: '',
2103
2104     collapsable : false,
2105     collapsed : false,
2106     rotateable : false,
2107     rotated : false,
2108     
2109     dragable : false,
2110     drag_group : false,
2111     dropable : false,
2112     drop_group : false,
2113     childContainer : false,
2114     dropEl : false, /// the dom placeholde element that indicates drop location.
2115     containerEl: false, // body container
2116     bodyEl: false, // card-body
2117     headerContainerEl : false, //
2118     headerEl : false,
2119     header_imageEl : false,
2120     
2121     
2122     layoutCls : function()
2123     {
2124         var cls = '';
2125         var t = this;
2126         Roo.log(this.margin_bottom.length);
2127         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2129             
2130             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2132             }
2133             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2135             }
2136         });
2137         
2138         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2141             }
2142         });
2143         
2144         // more generic support?
2145         if (this.hidden) {
2146             cls += ' d-none';
2147         }
2148         
2149         return cls;
2150     },
2151  
2152        // Roo.log("Call onRender: " + this.xtype);
2153         /*  We are looking at something like this.
2154 <div class="card">
2155     <img src="..." class="card-img-top" alt="...">
2156     <div class="card-body">
2157         <h5 class="card-title">Card title</h5>
2158          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2159
2160         >> this bit is really the body...
2161         <div> << we will ad dthis in hopefully it will not break shit.
2162         
2163         ** card text does not actually have any styling...
2164         
2165             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2166         
2167         </div> <<
2168           <a href="#" class="card-link">Card link</a>
2169           
2170     </div>
2171     <div class="card-footer">
2172         <small class="text-muted">Last updated 3 mins ago</small>
2173     </div>
2174 </div>
2175          */
2176     getAutoCreate : function(){
2177         
2178         var cfg = {
2179             tag : 'div',
2180             cls : 'card',
2181             cn : [ ]
2182         };
2183         
2184         if (this.weight.length && this.weight != 'light') {
2185             cfg.cls += ' text-white';
2186         } else {
2187             cfg.cls += ' text-dark'; // need as it's nested..
2188         }
2189         if (this.weight.length) {
2190             cfg.cls += ' bg-' + this.weight;
2191         }
2192         
2193         cfg.cls += ' ' + this.layoutCls(); 
2194         
2195         var hdr = false;
2196         var hdr_ctr = false;
2197         if (this.header.length) {
2198             hdr = {
2199                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2201                 cn : []
2202             };
2203             cfg.cn.push(hdr);
2204             hdr_ctr = hdr;
2205         } else {
2206             hdr = {
2207                 tag : 'div',
2208                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209                 cn : []
2210             };
2211             cfg.cn.push(hdr);
2212             hdr_ctr = hdr;
2213         }
2214         if (this.collapsable) {
2215             hdr_ctr = {
2216             tag : 'a',
2217             cls : 'd-block user-select-none',
2218             cn: [
2219                     {
2220                         tag: 'i',
2221                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2222                     }
2223                    
2224                 ]
2225             };
2226             hdr.cn.push(hdr_ctr);
2227         }
2228         
2229         hdr_ctr.cn.push(        {
2230             tag: 'span',
2231             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2232             html : this.header
2233         });
2234         
2235         
2236         if (this.header_image.length) {
2237             cfg.cn.push({
2238                 tag : 'img',
2239                 cls : 'card-img-top',
2240                 src: this.header_image // escape?
2241             });
2242         } else {
2243             cfg.cn.push({
2244                     tag : 'div',
2245                     cls : 'card-img-top d-none' 
2246                 });
2247         }
2248             
2249         var body = {
2250             tag : 'div',
2251             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2252             cn : []
2253         };
2254         var obody = body;
2255         if (this.collapsable || this.rotateable) {
2256             obody = {
2257                 tag: 'div',
2258                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2259                 cn : [  body ]
2260             };
2261         }
2262         
2263         cfg.cn.push(obody);
2264         
2265         if (this.title.length) {
2266             body.cn.push({
2267                 tag : 'div',
2268                 cls : 'card-title',
2269                 src: this.title // escape?
2270             });
2271         }  
2272         
2273         if (this.subtitle.length) {
2274             body.cn.push({
2275                 tag : 'div',
2276                 cls : 'card-title',
2277                 src: this.subtitle // escape?
2278             });
2279         }
2280         
2281         body.cn.push({
2282             tag : 'div',
2283             cls : 'roo-card-body-ctr'
2284         });
2285         
2286         if (this.html.length) {
2287             body.cn.push({
2288                 tag: 'div',
2289                 html : this.html
2290             });
2291         }
2292         // fixme ? handle objects?
2293         
2294         if (this.footer.length) {
2295            
2296             cfg.cn.push({
2297                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2298                 html : this.footer
2299             });
2300             
2301         } else {
2302             cfg.cn.push({cls : 'card-footer d-none'});
2303         }
2304         
2305         // footer...
2306         
2307         return cfg;
2308     },
2309     
2310     
2311     getCardHeader : function()
2312     {
2313         var  ret = this.el.select('.card-header',true).first();
2314         if (ret.hasClass('d-none')) {
2315             ret.removeClass('d-none');
2316         }
2317         
2318         return ret;
2319     },
2320     getCardFooter : function()
2321     {
2322         var  ret = this.el.select('.card-footer',true).first();
2323         if (ret.hasClass('d-none')) {
2324             ret.removeClass('d-none');
2325         }
2326         
2327         return ret;
2328     },
2329     getCardImageTop : function()
2330     {
2331         var  ret = this.header_imageEl;
2332         if (ret.hasClass('d-none')) {
2333             ret.removeClass('d-none');
2334         }
2335             
2336         return ret;
2337     },
2338     
2339     getChildContainer : function()
2340     {
2341         
2342         if(!this.el){
2343             return false;
2344         }
2345         return this.el.select('.roo-card-body-ctr',true).first();    
2346     },
2347     
2348     initEvents: function() 
2349     {
2350         this.bodyEl = this.el.select('.card-body',true).first(); 
2351         this.containerEl = this.getChildContainer();
2352         if(this.dragable){
2353             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354                     containerScroll: true,
2355                     ddGroup: this.drag_group || 'default_card_drag_group'
2356             });
2357             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2358         }
2359         if (this.dropable) {
2360             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361                 containerScroll: true,
2362                 ddGroup: this.drop_group || 'default_card_drag_group'
2363             });
2364             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2369         }
2370         
2371         if (this.collapsable) {
2372             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2373         }
2374         if (this.rotateable) {
2375             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2376         }
2377         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2378          
2379         this.footerEl = this.el.select('.card-footer',true).first();
2380         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382         this.headerEl = this.el.select('.card-header',true).first();
2383         
2384         if (this.rotated) {
2385             this.el.addClass('roo-card-rotated');
2386             this.fireEvent('rotate', this, true);
2387         }
2388         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2389         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2390         
2391     },
2392     getDragData : function(e)
2393     {
2394         var target = this.getEl();
2395         if (target) {
2396             //this.handleSelection(e);
2397             
2398             var dragData = {
2399                 source: this,
2400                 copy: false,
2401                 nodes: this.getEl(),
2402                 records: []
2403             };
2404             
2405             
2406             dragData.ddel = target.dom ;    // the div element
2407             Roo.log(target.getWidth( ));
2408             dragData.ddel.style.width = target.getWidth() + 'px';
2409             
2410             return dragData;
2411         }
2412         return false;
2413     },
2414     /**
2415     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2416     *    whole Element becomes the target, and this causes the drop gesture to append.
2417     *
2418     *    Returns an object:
2419     *     {
2420            
2421            position : 'below' or 'above'
2422            card  : relateive to card OBJECT (or true for no cards listed)
2423            items_n : relative to nth item in list
2424            card_n : relative to  nth card in list
2425     }
2426     *
2427     *    
2428     */
2429     getTargetFromEvent : function(e, dragged_card_el)
2430     {
2431         var target = e.getTarget();
2432         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433             target = target.parentNode;
2434         }
2435         
2436         var ret = {
2437             position: '',
2438             cards : [],
2439             card_n : -1,
2440             items_n : -1,
2441             card : false 
2442         };
2443         
2444         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445         // see if target is one of the 'cards'...
2446         
2447         
2448         //Roo.log(this.items.length);
2449         var pos = false;
2450         
2451         var last_card_n = 0;
2452         var cards_len  = 0;
2453         for (var i = 0;i< this.items.length;i++) {
2454             
2455             if (!this.items[i].el.hasClass('card')) {
2456                  continue;
2457             }
2458             pos = this.getDropPoint(e, this.items[i].el.dom);
2459             
2460             cards_len = ret.cards.length;
2461             //Roo.log(this.items[i].el.dom.id);
2462             ret.cards.push(this.items[i]);
2463             last_card_n  = i;
2464             if (ret.card_n < 0 && pos == 'above') {
2465                 ret.position = cards_len > 0 ? 'below' : pos;
2466                 ret.items_n = i > 0 ? i - 1 : 0;
2467                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2468                 ret.card = ret.cards[ret.card_n];
2469             }
2470         }
2471         if (!ret.cards.length) {
2472             ret.card = true;
2473             ret.position = 'below';
2474             ret.items_n;
2475             return ret;
2476         }
2477         // could not find a card.. stick it at the end..
2478         if (ret.card_n < 0) {
2479             ret.card_n = last_card_n;
2480             ret.card = ret.cards[last_card_n];
2481             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482             ret.position = 'below';
2483         }
2484         
2485         if (this.items[ret.items_n].el == dragged_card_el) {
2486             return false;
2487         }
2488         
2489         if (ret.position == 'below') {
2490             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2491             
2492             if (card_after  && card_after.el == dragged_card_el) {
2493                 return false;
2494             }
2495             return ret;
2496         }
2497         
2498         // its's after ..
2499         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2500         
2501         if (card_before  && card_before.el == dragged_card_el) {
2502             return false;
2503         }
2504         
2505         return ret;
2506     },
2507     
2508     onNodeEnter : function(n, dd, e, data){
2509         return false;
2510     },
2511     onNodeOver : function(n, dd, e, data)
2512     {
2513        
2514         var target_info = this.getTargetFromEvent(e,data.source.el);
2515         if (target_info === false) {
2516             this.dropPlaceHolder('hide');
2517             return false;
2518         }
2519         Roo.log(['getTargetFromEvent', target_info ]);
2520         
2521         
2522         if (this.fireEvent('cardover', this, [ data ]) === false) {
2523             return false;
2524         }
2525         
2526         this.dropPlaceHolder('show', target_info,data);
2527         
2528         return false; 
2529     },
2530     onNodeOut : function(n, dd, e, data){
2531         this.dropPlaceHolder('hide');
2532      
2533     },
2534     onNodeDrop : function(n, dd, e, data)
2535     {
2536         
2537         // call drop - return false if
2538         
2539         // this could actually fail - if the Network drops..
2540         // we will ignore this at present..- client should probably reload
2541         // the whole set of cards if stuff like that fails.
2542         
2543         
2544         var info = this.getTargetFromEvent(e,data.source.el);
2545         if (info === false) {
2546             return false;
2547         }
2548         this.dropPlaceHolder('hide');
2549   
2550           
2551     
2552         this.acceptCard(data.source, info.position, info.card, info.items_n);
2553         return true;
2554          
2555     },
2556     firstChildCard : function()
2557     {
2558         for (var i = 0;i< this.items.length;i++) {
2559             
2560             if (!this.items[i].el.hasClass('card')) {
2561                  continue;
2562             }
2563             return this.items[i];
2564         }
2565         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2566     },
2567     /**
2568      * accept card
2569      *
2570      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2571      */
2572     acceptCard : function(move_card,  position, next_to_card )
2573     {
2574         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2575             return false;
2576         }
2577         
2578         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2579         
2580         move_card.parent().removeCard(move_card);
2581         
2582         
2583         var dom = move_card.el.dom;
2584         dom.style.width = ''; // clear with - which is set by drag.
2585         
2586         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587             var cardel = next_to_card.el.dom;
2588             
2589             if (position == 'above' ) {
2590                 cardel.parentNode.insertBefore(dom, cardel);
2591             } else if (cardel.nextSibling) {
2592                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2593             } else {
2594                 cardel.parentNode.append(dom);
2595             }
2596         } else {
2597             // card container???
2598             this.containerEl.dom.append(dom);
2599         }
2600         
2601         //FIXME HANDLE card = true 
2602         
2603         // add this to the correct place in items.
2604         
2605         // remove Card from items.
2606         
2607        
2608         if (this.items.length) {
2609             var nitems = [];
2610             //Roo.log([info.items_n, info.position, this.items.length]);
2611             for (var i =0; i < this.items.length; i++) {
2612                 if (i == to_items_n && position == 'above') {
2613                     nitems.push(move_card);
2614                 }
2615                 nitems.push(this.items[i]);
2616                 if (i == to_items_n && position == 'below') {
2617                     nitems.push(move_card);
2618                 }
2619             }
2620             this.items = nitems;
2621             Roo.log(this.items);
2622         } else {
2623             this.items.push(move_card);
2624         }
2625         
2626         move_card.parentId = this.id;
2627         
2628         return true;
2629         
2630         
2631     },
2632     removeCard : function(c)
2633     {
2634         this.items = this.items.filter(function(e) { return e != c });
2635  
2636         var dom = c.el.dom;
2637         dom.parentNode.removeChild(dom);
2638         dom.style.width = ''; // clear with - which is set by drag.
2639         c.parentId = false;
2640         
2641     },
2642     
2643     /**    Decide whether to drop above or below a View node. */
2644     getDropPoint : function(e, n, dd)
2645     {
2646         if (dd) {
2647              return false;
2648         }
2649         if (n == this.containerEl.dom) {
2650             return "above";
2651         }
2652         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653         var c = t + (b - t) / 2;
2654         var y = Roo.lib.Event.getPageY(e);
2655         if(y <= c) {
2656             return "above";
2657         }else{
2658             return "below";
2659         }
2660     },
2661     onToggleCollapse : function(e)
2662         {
2663         if (this.collapsed) {
2664             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665             this.collapsableEl.addClass('show');
2666             this.collapsed = false;
2667             return;
2668         }
2669         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670         this.collapsableEl.removeClass('show');
2671         this.collapsed = true;
2672         
2673     
2674     },
2675     
2676     onToggleRotate : function(e)
2677     {
2678         this.collapsableEl.removeClass('show');
2679         this.footerEl.removeClass('d-none');
2680         this.el.removeClass('roo-card-rotated');
2681         this.el.removeClass('d-none');
2682         if (this.rotated) {
2683             
2684             this.collapsableEl.addClass('show');
2685             this.rotated = false;
2686             this.fireEvent('rotate', this, this.rotated);
2687             return;
2688         }
2689         this.el.addClass('roo-card-rotated');
2690         this.footerEl.addClass('d-none');
2691         this.el.select('.roo-collapsable').removeClass('show');
2692         
2693         this.rotated = true;
2694         this.fireEvent('rotate', this, this.rotated);
2695     
2696     },
2697     
2698     dropPlaceHolder: function (action, info, data)
2699     {
2700         if (this.dropEl === false) {
2701             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2702             cls : 'd-none'
2703             },true);
2704         }
2705         this.dropEl.removeClass(['d-none', 'd-block']);        
2706         if (action == 'hide') {
2707             
2708             this.dropEl.addClass('d-none');
2709             return;
2710         }
2711         // FIXME - info.card == true!!!
2712         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2713         
2714         if (info.card !== true) {
2715             var cardel = info.card.el.dom;
2716             
2717             if (info.position == 'above') {
2718                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719             } else if (cardel.nextSibling) {
2720                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2721             } else {
2722                 cardel.parentNode.append(this.dropEl.dom);
2723             }
2724         } else {
2725             // card container???
2726             this.containerEl.dom.append(this.dropEl.dom);
2727         }
2728         
2729         this.dropEl.addClass('d-block roo-card-dropzone');
2730         
2731         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2732         
2733         
2734     
2735     
2736     
2737     },
2738     setHeaderText: function(html)
2739     {
2740         this.header = html;
2741         if (this.headerContainerEl) {
2742             this.headerContainerEl.dom.innerHTML = html;
2743         }
2744     },
2745     onHeaderImageLoad : function(ev, he)
2746     {
2747         if (!this.header_image_fit_square) {
2748             return;
2749         }
2750         
2751         var hw = he.naturalHeight / he.naturalWidth;
2752         // wide image = < 0
2753         // tall image = > 1
2754         //var w = he.dom.naturalWidth;
2755         var ww = he.width;
2756         he.style.left =  0;
2757         he.style.position =  'relative';
2758         if (hw > 1) {
2759             var nw = (ww * (1/hw));
2760             Roo.get(he).setSize( ww * (1/hw),  ww);
2761             he.style.left =  ((ww - nw)/ 2) + 'px';
2762             he.style.position =  'relative';
2763         }
2764
2765     }
2766
2767     
2768 });
2769
2770 /*
2771  * - LGPL
2772  *
2773  * Card header - holder for the card header elements.
2774  * 
2775  */
2776
2777 /**
2778  * @class Roo.bootstrap.CardHeader
2779  * @extends Roo.bootstrap.Element
2780  * @parent Roo.bootstrap.Card
2781  * @children Roo.bootstrap.Component
2782  * Bootstrap CardHeader class
2783  * @constructor
2784  * Create a new Card Header - that you can embed children into
2785  * @param {Object} config The config object
2786  */
2787
2788 Roo.bootstrap.CardHeader = function(config){
2789     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2790 };
2791
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2793     
2794     
2795     container_method : 'getCardHeader' 
2796     
2797      
2798     
2799     
2800    
2801 });
2802
2803  
2804
2805  /*
2806  * - LGPL
2807  *
2808  * Card footer - holder for the card footer elements.
2809  * 
2810  */
2811
2812 /**
2813  * @class Roo.bootstrap.CardFooter
2814  * @extends Roo.bootstrap.Element
2815  * @parent Roo.bootstrap.Card
2816  * @children Roo.bootstrap.Component
2817  * Bootstrap CardFooter class
2818  * 
2819  * @constructor
2820  * Create a new Card Footer - that you can embed children into
2821  * @param {Object} config The config object
2822  */
2823
2824 Roo.bootstrap.CardFooter = function(config){
2825     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2826 };
2827
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2829     
2830     
2831     container_method : 'getCardFooter' 
2832     
2833      
2834     
2835     
2836    
2837 });
2838
2839  
2840
2841  /*
2842  * - LGPL
2843  *
2844  * Card header - holder for the card header elements.
2845  * 
2846  */
2847
2848 /**
2849  * @class Roo.bootstrap.CardImageTop
2850  * @extends Roo.bootstrap.Element
2851  * @parent Roo.bootstrap.Card
2852  * @children Roo.bootstrap.Component
2853  * Bootstrap CardImageTop class
2854  * 
2855  * @constructor
2856  * Create a new Card Image Top container
2857  * @param {Object} config The config object
2858  */
2859
2860 Roo.bootstrap.CardImageTop = function(config){
2861     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2862 };
2863
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2865     
2866    
2867     container_method : 'getCardImageTop' 
2868     
2869      
2870     
2871    
2872 });
2873
2874  
2875
2876  
2877 /*
2878 * Licence: LGPL
2879 */
2880
2881 /**
2882  * @class Roo.bootstrap.ButtonUploader
2883  * @extends Roo.bootstrap.Button
2884  * Bootstrap Button Uploader class - it's a button which when you add files to it
2885  *
2886  * 
2887  * @cfg {Number} errorTimeout default 3000
2888  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2889  * @cfg {Array}  html The button text.
2890  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2891  *
2892  * @constructor
2893  * Create a new CardUploader
2894  * @param {Object} config The config object
2895  */
2896
2897 Roo.bootstrap.ButtonUploader = function(config){
2898     
2899  
2900     
2901     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2902     
2903      
2904      this.addEvents({
2905          // raw events
2906         /**
2907          * @event beforeselect
2908          * When button is pressed, before show upload files dialog is shown
2909          * @param {Roo.bootstrap.UploaderButton} this
2910          *
2911          */
2912         'beforeselect' : true,
2913          /**
2914          * @event fired when files have been selected, 
2915          * When a the download link is clicked
2916          * @param {Roo.bootstrap.UploaderButton} this
2917          * @param {Array} Array of files that have been uploaded
2918          */
2919         'uploaded' : true
2920         
2921     });
2922 };
2923  
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2925     
2926      
2927     errorTimeout : 3000,
2928      
2929     images : false,
2930    
2931     fileCollection : false,
2932     allowBlank : true,
2933     
2934     multiple : true,
2935     
2936     getAutoCreate : function()
2937     {
2938         var im = {
2939             tag: 'input',
2940             type : 'file',
2941             cls : 'd-none  roo-card-upload-selector' 
2942           
2943         };
2944         if (this.multiple) {
2945             im.multiple = 'multiple';
2946         }
2947         
2948         return  {
2949             cls :'div' ,
2950             cn : [
2951                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2952                 im
2953
2954             ]
2955         };
2956            
2957          
2958     },
2959      
2960    
2961     initEvents : function()
2962     {
2963         
2964         Roo.bootstrap.Button.prototype.initEvents.call(this);
2965         
2966         
2967         
2968         
2969         
2970         this.urlAPI = (window.createObjectURL && window) || 
2971                                 (window.URL && URL.revokeObjectURL && URL) || 
2972                                 (window.webkitURL && webkitURL);
2973                         
2974          
2975          
2976          
2977         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2978         
2979         this.selectorEl.on('change', this.onFileSelected, this);
2980          
2981          
2982        
2983     },
2984     
2985    
2986     onClick : function(e)
2987     {
2988         e.preventDefault();
2989         
2990         if ( this.fireEvent('beforeselect', this) === false) {
2991             return;
2992         }
2993          
2994         this.selectorEl.dom.click();
2995          
2996     },
2997     
2998     onFileSelected : function(e)
2999     {
3000         e.preventDefault();
3001         
3002         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003             return;
3004         }
3005         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006         this.selectorEl.dom.value  = '';// hopefully reset..
3007         
3008         this.fireEvent('uploaded', this,  files );
3009         
3010     },
3011     
3012        
3013    
3014     
3015     /**
3016      * addCard - add an Attachment to the uploader
3017      * @param data - the data about the image to upload
3018      *
3019      * {
3020           id : 123
3021           title : "Title of file",
3022           is_uploaded : false,
3023           src : "http://.....",
3024           srcfile : { the File upload object },
3025           mimetype : file.type,
3026           preview : false,
3027           is_deleted : 0
3028           .. any other data...
3029         }
3030      *
3031      * 
3032     */
3033      
3034     reset: function()
3035     {
3036          
3037          this.selectorEl
3038     } 
3039     
3040     
3041     
3042     
3043 });
3044  /*
3045  * - LGPL
3046  *
3047  * image
3048  * 
3049  */
3050
3051
3052 /**
3053  * @class Roo.bootstrap.Img
3054  * @extends Roo.bootstrap.Component
3055  * Bootstrap Img class
3056  * @cfg {Boolean} imgResponsive false | true
3057  * @cfg {String} border rounded | circle | thumbnail
3058  * @cfg {String} src image source
3059  * @cfg {String} alt image alternative text
3060  * @cfg {String} href a tag href
3061  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062  * @cfg {String} xsUrl xs image source
3063  * @cfg {String} smUrl sm image source
3064  * @cfg {String} mdUrl md image source
3065  * @cfg {String} lgUrl lg image source
3066  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067  * 
3068  * @constructor
3069  * Create a new Input
3070  * @param {Object} config The config object
3071  */
3072
3073 Roo.bootstrap.Img = function(config){
3074     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3075     
3076     this.addEvents({
3077         // img events
3078         /**
3079          * @event click
3080          * The img click event for the img.
3081          * @param {Roo.EventObject} e
3082          */
3083         "click" : true,
3084         /**
3085          * @event load
3086          * The when any image loads
3087          * @param {Roo.EventObject} e
3088          */
3089         "load" : true
3090     });
3091 };
3092
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3094     
3095     imgResponsive: true,
3096     border: '',
3097     src: 'about:blank',
3098     href: false,
3099     target: false,
3100     xsUrl: '',
3101     smUrl: '',
3102     mdUrl: '',
3103     lgUrl: '',
3104     backgroundContain : false,
3105
3106     getAutoCreate : function()
3107     {   
3108         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109             return this.createSingleImg();
3110         }
3111         
3112         var cfg = {
3113             tag: 'div',
3114             cls: 'roo-image-responsive-group',
3115             cn: []
3116         };
3117         var _this = this;
3118         
3119         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3120             
3121             if(!_this[size + 'Url']){
3122                 return;
3123             }
3124             
3125             var img = {
3126                 tag: 'img',
3127                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128                 html: _this.html || cfg.html,
3129                 src: _this[size + 'Url']
3130             };
3131             
3132             img.cls += ' roo-image-responsive-' + size;
3133             
3134             var s = ['xs', 'sm', 'md', 'lg'];
3135             
3136             s.splice(s.indexOf(size), 1);
3137             
3138             Roo.each(s, function(ss){
3139                 img.cls += ' hidden-' + ss;
3140             });
3141             
3142             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143                 cfg.cls += ' img-' + _this.border;
3144             }
3145             
3146             if(_this.alt){
3147                 cfg.alt = _this.alt;
3148             }
3149             
3150             if(_this.href){
3151                 var a = {
3152                     tag: 'a',
3153                     href: _this.href,
3154                     cn: [
3155                         img
3156                     ]
3157                 };
3158
3159                 if(this.target){
3160                     a.target = _this.target;
3161                 }
3162             }
3163             
3164             cfg.cn.push((_this.href) ? a : img);
3165             
3166         });
3167         
3168         return cfg;
3169     },
3170     
3171     createSingleImg : function()
3172     {
3173         var cfg = {
3174             tag: 'img',
3175             cls: (this.imgResponsive) ? 'img-responsive' : '',
3176             html : null,
3177             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3178         };
3179         
3180         if (this.backgroundContain) {
3181             cfg.cls += ' background-contain';
3182         }
3183         
3184         cfg.html = this.html || cfg.html;
3185         
3186         if (this.backgroundContain) {
3187             cfg.style="background-image: url(" + this.src + ')';
3188         } else {
3189             cfg.src = this.src || cfg.src;
3190         }
3191         
3192         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193             cfg.cls += ' img-' + this.border;
3194         }
3195         
3196         if(this.alt){
3197             cfg.alt = this.alt;
3198         }
3199         
3200         if(this.href){
3201             var a = {
3202                 tag: 'a',
3203                 href: this.href,
3204                 cn: [
3205                     cfg
3206                 ]
3207             };
3208             
3209             if(this.target){
3210                 a.target = this.target;
3211             }
3212             
3213         }
3214         
3215         return (this.href) ? a : cfg;
3216     },
3217     
3218     initEvents: function() 
3219     {
3220         if(!this.href){
3221             this.el.on('click', this.onClick, this);
3222         }
3223         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224             this.el.on('load', this.onImageLoad, this);
3225         } else {
3226             // not sure if this works.. not tested
3227             this.el.select('img', true).on('load', this.onImageLoad, this);
3228         }
3229         
3230     },
3231     
3232     onClick : function(e)
3233     {
3234         Roo.log('img onclick');
3235         this.fireEvent('click', this, e);
3236     },
3237     onImageLoad: function(e)
3238     {
3239         Roo.log('img load');
3240         this.fireEvent('load', this, e);
3241     },
3242     
3243     /**
3244      * Sets the url of the image - used to update it
3245      * @param {String} url the url of the image
3246      */
3247     
3248     setSrc : function(url)
3249     {
3250         this.src =  url;
3251         
3252         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253             if (this.backgroundContain) {
3254                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3255             } else {
3256                 this.el.dom.src =  url;
3257             }
3258             return;
3259         }
3260         
3261         this.el.select('img', true).first().dom.src =  url;
3262     }
3263     
3264     
3265    
3266 });
3267
3268  /*
3269  * - LGPL
3270  *
3271  * image
3272  * 
3273  */
3274
3275
3276 /**
3277  * @class Roo.bootstrap.Link
3278  * @extends Roo.bootstrap.Component
3279  * @children Roo.bootstrap.Component
3280  * Bootstrap Link Class (eg. '<a href>')
3281  
3282  * @cfg {String} alt image alternative text
3283  * @cfg {String} href a tag href
3284  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285  * @cfg {String} html the content of the link.
3286  * @cfg {String} anchor name for the anchor link
3287  * @cfg {String} fa - favicon
3288
3289  * @cfg {Boolean} preventDefault (true | false) default false
3290
3291  * 
3292  * @constructor
3293  * Create a new Input
3294  * @param {Object} config The config object
3295  */
3296
3297 Roo.bootstrap.Link = function(config){
3298     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3299     
3300     this.addEvents({
3301         // img events
3302         /**
3303          * @event click
3304          * The img click event for the img.
3305          * @param {Roo.EventObject} e
3306          */
3307         "click" : true
3308     });
3309 };
3310
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3312     
3313     href: false,
3314     target: false,
3315     preventDefault: false,
3316     anchor : false,
3317     alt : false,
3318     fa: false,
3319
3320
3321     getAutoCreate : function()
3322     {
3323         var html = this.html || '';
3324         
3325         if (this.fa !== false) {
3326             html = '<i class="fa fa-' + this.fa + '"></i>';
3327         }
3328         var cfg = {
3329             tag: 'a'
3330         };
3331         // anchor's do not require html/href...
3332         if (this.anchor === false) {
3333             cfg.html = html;
3334             cfg.href = this.href || '#';
3335         } else {
3336             cfg.name = this.anchor;
3337             if (this.html !== false || this.fa !== false) {
3338                 cfg.html = html;
3339             }
3340             if (this.href !== false) {
3341                 cfg.href = this.href;
3342             }
3343         }
3344         
3345         if(this.alt !== false){
3346             cfg.alt = this.alt;
3347         }
3348         
3349         
3350         if(this.target !== false) {
3351             cfg.target = this.target;
3352         }
3353         
3354         return cfg;
3355     },
3356     
3357     initEvents: function() {
3358         
3359         if(!this.href || this.preventDefault){
3360             this.el.on('click', this.onClick, this);
3361         }
3362     },
3363     
3364     onClick : function(e)
3365     {
3366         if(this.preventDefault){
3367             e.preventDefault();
3368         }
3369         //Roo.log('img onclick');
3370         this.fireEvent('click', this, e);
3371     }
3372    
3373 });
3374
3375  /*
3376  * - LGPL
3377  *
3378  * header
3379  * 
3380  */
3381
3382 /**
3383  * @class Roo.bootstrap.Header
3384  * @extends Roo.bootstrap.Component
3385  * @children Roo.bootstrap.Component
3386  * Bootstrap Header class
3387  *
3388  * 
3389  * @cfg {String} html content of header
3390  * @cfg {Number} level (1|2|3|4|5|6) default 1
3391  * 
3392  * @constructor
3393  * Create a new Header
3394  * @param {Object} config The config object
3395  */
3396
3397
3398 Roo.bootstrap.Header  = function(config){
3399     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 };
3401
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3403     
3404     //href : false,
3405     html : false,
3406     level : 1,
3407     
3408     
3409     
3410     getAutoCreate : function(){
3411         
3412         
3413         
3414         var cfg = {
3415             tag: 'h' + (1 *this.level),
3416             html: this.html || ''
3417         } ;
3418         
3419         return cfg;
3420     }
3421    
3422 });
3423
3424  
3425
3426  /**
3427  * @class Roo.bootstrap.MenuMgr
3428  * @licence LGPL
3429  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430  * @static
3431  */
3432 Roo.bootstrap.menu.Manager = function(){
3433    var menus, active, groups = {}, attached = false, lastShow = new Date();
3434
3435    // private - called when first menu is created
3436    function init(){
3437        menus = {};
3438        active = new Roo.util.MixedCollection();
3439        Roo.get(document).addKeyListener(27, function(){
3440            if(active.length > 0){
3441                hideAll();
3442            }
3443        });
3444    }
3445
3446    // private
3447    function hideAll(){
3448        if(active && active.length > 0){
3449            var c = active.clone();
3450            c.each(function(m){
3451                m.hide();
3452            });
3453        }
3454    }
3455
3456    // private
3457    function onHide(m){
3458        active.remove(m);
3459        if(active.length < 1){
3460            Roo.get(document).un("mouseup", onMouseDown);
3461             
3462            attached = false;
3463        }
3464    }
3465
3466    // private
3467    function onShow(m){
3468        var last = active.last();
3469        lastShow = new Date();
3470        active.add(m);
3471        if(!attached){
3472           Roo.get(document).on("mouseup", onMouseDown);
3473            
3474            attached = true;
3475        }
3476        if(m.parentMenu){
3477           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478           m.parentMenu.activeChild = m;
3479        }else if(last && last.isVisible()){
3480           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3481        }
3482    }
3483
3484    // private
3485    function onBeforeHide(m){
3486        if(m.activeChild){
3487            m.activeChild.hide();
3488        }
3489        if(m.autoHideTimer){
3490            clearTimeout(m.autoHideTimer);
3491            delete m.autoHideTimer;
3492        }
3493    }
3494
3495    // private
3496    function onBeforeShow(m){
3497        var pm = m.parentMenu;
3498        if(!pm && !m.allowOtherMenus){
3499            hideAll();
3500        }else if(pm && pm.activeChild && active != m){
3501            pm.activeChild.hide();
3502        }
3503    }
3504
3505    // private this should really trigger on mouseup..
3506    function onMouseDown(e){
3507         Roo.log("on Mouse Up");
3508         
3509         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510             Roo.log("MenuManager hideAll");
3511             hideAll();
3512             e.stopEvent();
3513         }
3514         
3515         
3516    }
3517
3518    // private
3519    function onBeforeCheck(mi, state){
3520        if(state){
3521            var g = groups[mi.group];
3522            for(var i = 0, l = g.length; i < l; i++){
3523                if(g[i] != mi){
3524                    g[i].setChecked(false);
3525                }
3526            }
3527        }
3528    }
3529
3530    return {
3531
3532        /**
3533         * Hides all menus that are currently visible
3534         */
3535        hideAll : function(){
3536             hideAll();  
3537        },
3538
3539        // private
3540        register : function(menu){
3541            if(!menus){
3542                init();
3543            }
3544            menus[menu.id] = menu;
3545            menu.on("beforehide", onBeforeHide);
3546            menu.on("hide", onHide);
3547            menu.on("beforeshow", onBeforeShow);
3548            menu.on("show", onShow);
3549            var g = menu.group;
3550            if(g && menu.events["checkchange"]){
3551                if(!groups[g]){
3552                    groups[g] = [];
3553                }
3554                groups[g].push(menu);
3555                menu.on("checkchange", onCheck);
3556            }
3557        },
3558
3559         /**
3560          * Returns a {@link Roo.menu.Menu} object
3561          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562          * be used to generate and return a new Menu instance.
3563          */
3564        get : function(menu){
3565            if(typeof menu == "string"){ // menu id
3566                return menus[menu];
3567            }else if(menu.events){  // menu instance
3568                return menu;
3569            }
3570            /*else if(typeof menu.length == 'number'){ // array of menu items?
3571                return new Roo.bootstrap.Menu({items:menu});
3572            }else{ // otherwise, must be a config
3573                return new Roo.bootstrap.Menu(menu);
3574            }
3575            */
3576            return false;
3577        },
3578
3579        // private
3580        unregister : function(menu){
3581            delete menus[menu.id];
3582            menu.un("beforehide", onBeforeHide);
3583            menu.un("hide", onHide);
3584            menu.un("beforeshow", onBeforeShow);
3585            menu.un("show", onShow);
3586            var g = menu.group;
3587            if(g && menu.events["checkchange"]){
3588                groups[g].remove(menu);
3589                menu.un("checkchange", onCheck);
3590            }
3591        },
3592
3593        // private
3594        registerCheckable : function(menuItem){
3595            var g = menuItem.group;
3596            if(g){
3597                if(!groups[g]){
3598                    groups[g] = [];
3599                }
3600                groups[g].push(menuItem);
3601                menuItem.on("beforecheckchange", onBeforeCheck);
3602            }
3603        },
3604
3605        // private
3606        unregisterCheckable : function(menuItem){
3607            var g = menuItem.group;
3608            if(g){
3609                groups[g].remove(menuItem);
3610                menuItem.un("beforecheckchange", onBeforeCheck);
3611            }
3612        }
3613    };
3614 }(); 
3615 /**
3616  * @class Roo.bootstrap.menu.Menu
3617  * @extends Roo.bootstrap.Component
3618  * @licence LGPL
3619  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3620  * @parent none
3621  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3622  * 
3623  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3625  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3626  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3628 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3629  
3630  * @constructor
3631  * Create a new Menu
3632  * @param {Object} config The config objectQ
3633  */
3634
3635
3636 Roo.bootstrap.menu.Menu = function(config){
3637     
3638     if (config.type == 'treeview') {
3639         // normally menu's are drawn attached to the document to handle layering etc..
3640         // however treeview (used by the docs menu is drawn into the parent element)
3641         this.container_method = 'getChildContainer'; 
3642     }
3643     
3644     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645     if (this.registerMenu && this.type != 'treeview')  {
3646         Roo.bootstrap.menu.Manager.register(this);
3647     }
3648     
3649     
3650     this.addEvents({
3651         /**
3652          * @event beforeshow
3653          * Fires before this menu is displayed (return false to block)
3654          * @param {Roo.menu.Menu} this
3655          */
3656         beforeshow : true,
3657         /**
3658          * @event beforehide
3659          * Fires before this menu is hidden (return false to block)
3660          * @param {Roo.menu.Menu} this
3661          */
3662         beforehide : true,
3663         /**
3664          * @event show
3665          * Fires after this menu is displayed
3666          * @param {Roo.menu.Menu} this
3667          */
3668         show : true,
3669         /**
3670          * @event hide
3671          * Fires after this menu is hidden
3672          * @param {Roo.menu.Menu} this
3673          */
3674         hide : true,
3675         /**
3676          * @event click
3677          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678          * @param {Roo.menu.Menu} this
3679          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680          * @param {Roo.EventObject} e
3681          */
3682         click : true,
3683         /**
3684          * @event mouseover
3685          * Fires when the mouse is hovering over this menu
3686          * @param {Roo.menu.Menu} this
3687          * @param {Roo.EventObject} e
3688          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689          */
3690         mouseover : true,
3691         /**
3692          * @event mouseout
3693          * Fires when the mouse exits this menu
3694          * @param {Roo.menu.Menu} this
3695          * @param {Roo.EventObject} e
3696          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3697          */
3698         mouseout : true,
3699         /**
3700          * @event itemclick
3701          * Fires when a menu item contained in this menu is clicked
3702          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703          * @param {Roo.EventObject} e
3704          */
3705         itemclick: true
3706     });
3707     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 };
3709
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3711     
3712    /// html : false,
3713    
3714     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3715     type: false,
3716     /**
3717      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718      */
3719     registerMenu : true,
3720     
3721     menuItems :false, // stores the menu items..
3722     
3723     hidden:true,
3724         
3725     parentMenu : false,
3726     
3727     stopEvent : true,
3728     
3729     isLink : false,
3730     
3731     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732     
3733     hideTrigger : false,
3734     
3735     align : 'tl-bl?',
3736     
3737     
3738     getChildContainer : function() {
3739         return this.el;  
3740     },
3741     
3742     getAutoCreate : function(){
3743          
3744         //if (['right'].indexOf(this.align)!==-1) {
3745         //    cfg.cn[1].cls += ' pull-right'
3746         //}
3747          
3748         var cfg = {
3749             tag : 'ul',
3750             cls : 'dropdown-menu shadow' ,
3751             style : 'z-index:1000'
3752             
3753         };
3754         
3755         if (this.type === 'submenu') {
3756             cfg.cls = 'submenu active';
3757         }
3758         if (this.type === 'treeview') {
3759             cfg.cls = 'treeview-menu';
3760         }
3761         
3762         return cfg;
3763     },
3764     initEvents : function() {
3765         
3766        // Roo.log("ADD event");
3767        // Roo.log(this.triggerEl.dom);
3768         if (this.triggerEl) {
3769             
3770             this.triggerEl.on('click', this.onTriggerClick, this);
3771             
3772             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773             
3774             if (!this.hideTrigger) {
3775                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776                     // dropdown toggle on the 'a' in BS4?
3777                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778                 } else {
3779                     this.triggerEl.addClass('dropdown-toggle');
3780                 }
3781             }
3782         }
3783         
3784         if (Roo.isTouch) {
3785             this.el.on('touchstart'  , this.onTouch, this);
3786         }
3787         this.el.on('click' , this.onClick, this);
3788
3789         this.el.on("mouseover", this.onMouseOver, this);
3790         this.el.on("mouseout", this.onMouseOut, this);
3791         
3792     },
3793     
3794     findTargetItem : function(e)
3795     {
3796         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3797         if(!t){
3798             return false;
3799         }
3800         //Roo.log(t);         Roo.log(t.id);
3801         if(t && t.id){
3802             //Roo.log(this.menuitems);
3803             return this.menuitems.get(t.id);
3804             
3805             //return this.items.get(t.menuItemId);
3806         }
3807         
3808         return false;
3809     },
3810     
3811     onTouch : function(e) 
3812     {
3813         Roo.log("menu.onTouch");
3814         //e.stopEvent(); this make the user popdown broken
3815         this.onClick(e);
3816     },
3817     
3818     onClick : function(e)
3819     {
3820         Roo.log("menu.onClick");
3821         
3822         var t = this.findTargetItem(e);
3823         if(!t || t.isContainer){
3824             return;
3825         }
3826         Roo.log(e);
3827         /*
3828         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3829             if(t == this.activeItem && t.shouldDeactivate(e)){
3830                 this.activeItem.deactivate();
3831                 delete this.activeItem;
3832                 return;
3833             }
3834             if(t.canActivate){
3835                 this.setActiveItem(t, true);
3836             }
3837             return;
3838             
3839             
3840         }
3841         */
3842        
3843         Roo.log('pass click event');
3844         
3845         t.onClick(e);
3846         
3847         this.fireEvent("click", this, t, e);
3848         
3849         var _this = this;
3850         
3851         if(!t.href.length || t.href == '#'){
3852             (function() { _this.hide(); }).defer(100);
3853         }
3854         
3855     },
3856     
3857     onMouseOver : function(e){
3858         var t  = this.findTargetItem(e);
3859         //Roo.log(t);
3860         //if(t){
3861         //    if(t.canActivate && !t.disabled){
3862         //        this.setActiveItem(t, true);
3863         //    }
3864         //}
3865         
3866         this.fireEvent("mouseover", this, e, t);
3867     },
3868     isVisible : function(){
3869         return !this.hidden;
3870     },
3871     onMouseOut : function(e){
3872         var t  = this.findTargetItem(e);
3873         
3874         //if(t ){
3875         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3876         //        this.activeItem.deactivate();
3877         //        delete this.activeItem;
3878         //    }
3879         //}
3880         this.fireEvent("mouseout", this, e, t);
3881     },
3882     
3883     
3884     /**
3885      * Displays this menu relative to another element
3886      * @param {String/HTMLElement/Roo.Element} element The element to align to
3887      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888      * the element (defaults to this.defaultAlign)
3889      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890      */
3891     show : function(el, pos, parentMenu)
3892     {
3893         if (false === this.fireEvent("beforeshow", this)) {
3894             Roo.log("show canceled");
3895             return;
3896         }
3897         this.parentMenu = parentMenu;
3898         if(!this.el){
3899             this.render();
3900         }
3901         this.el.addClass('show'); // show otherwise we do not know how big we are..
3902          
3903         var xy = this.el.getAlignToXY(el, pos);
3904         
3905         // bl-tl << left align  below
3906         // tl-bl << left align 
3907         
3908         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909             // if it goes to far to the right.. -> align left.
3910             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911         }
3912         if(xy[0] < 0){
3913             // was left align - go right?
3914             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915         }
3916         
3917         // goes down the bottom
3918         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919            xy[1]  < 0 ){
3920             var a = this.align.replace('?', '').split('-');
3921             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3922             
3923         }
3924         
3925         this.showAt(  xy , parentMenu, false);
3926     },
3927      /**
3928      * Displays this menu at a specific xy position
3929      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931      */
3932     showAt : function(xy, parentMenu, /* private: */_e){
3933         this.parentMenu = parentMenu;
3934         if(!this.el){
3935             this.render();
3936         }
3937         if(_e !== false){
3938             this.fireEvent("beforeshow", this);
3939             //xy = this.el.adjustForConstraints(xy);
3940         }
3941         
3942         //this.el.show();
3943         this.hideMenuItems();
3944         this.hidden = false;
3945         if (this.triggerEl) {
3946             this.triggerEl.addClass('open');
3947         }
3948         
3949         this.el.addClass('show');
3950         
3951         
3952         
3953         // reassign x when hitting right
3954         
3955         // reassign y when hitting bottom
3956         
3957         // but the list may align on trigger left or trigger top... should it be a properity?
3958         
3959         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3960             this.el.setXY(xy);
3961         }
3962         
3963         this.focus();
3964         this.fireEvent("show", this);
3965     },
3966     
3967     focus : function(){
3968         return;
3969         if(!this.hidden){
3970             this.doFocus.defer(50, this);
3971         }
3972     },
3973
3974     doFocus : function(){
3975         if(!this.hidden){
3976             this.focusEl.focus();
3977         }
3978     },
3979
3980     /**
3981      * Hides this menu and optionally all parent menus
3982      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983      */
3984     hide : function(deep)
3985     {
3986         if (false === this.fireEvent("beforehide", this)) {
3987             Roo.log("hide canceled");
3988             return;
3989         }
3990         this.hideMenuItems();
3991         if(this.el && this.isVisible()){
3992            
3993             if(this.activeItem){
3994                 this.activeItem.deactivate();
3995                 this.activeItem = null;
3996             }
3997             if (this.triggerEl) {
3998                 this.triggerEl.removeClass('open');
3999             }
4000             
4001             this.el.removeClass('show');
4002             this.hidden = true;
4003             this.fireEvent("hide", this);
4004         }
4005         if(deep === true && this.parentMenu){
4006             this.parentMenu.hide(true);
4007         }
4008     },
4009     
4010     onTriggerClick : function(e)
4011     {
4012         Roo.log('trigger click');
4013         
4014         var target = e.getTarget();
4015         
4016         Roo.log(target.nodeName.toLowerCase());
4017         
4018         if(target.nodeName.toLowerCase() === 'i'){
4019             e.preventDefault();
4020         }
4021         
4022     },
4023     
4024     onTriggerPress  : function(e)
4025     {
4026         Roo.log('trigger press');
4027         //Roo.log(e.getTarget());
4028        // Roo.log(this.triggerEl.dom);
4029        
4030         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031         var pel = Roo.get(e.getTarget());
4032         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033             Roo.log('is treeview or dropdown?');
4034             return;
4035         }
4036         
4037         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4038             return;
4039         }
4040         
4041         if (this.isVisible()) {
4042             Roo.log('hide');
4043             this.hide();
4044         } else {
4045             Roo.log('show');
4046             
4047             this.show(this.triggerEl, this.align, false);
4048         }
4049         
4050         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4051             e.stopEvent();
4052         }
4053         
4054     },
4055        
4056     
4057     hideMenuItems : function()
4058     {
4059         Roo.log("hide Menu Items");
4060         if (!this.el) { 
4061             return;
4062         }
4063         
4064         this.el.select('.open',true).each(function(aa) {
4065             
4066             aa.removeClass('open');
4067          
4068         });
4069     },
4070     addxtypeChild : function (tree, cntr) {
4071         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072           
4073         this.menuitems.add(comp);
4074         return comp;
4075
4076     },
4077     getEl : function()
4078     {
4079         Roo.log(this.el);
4080         return this.el;
4081     },
4082     
4083     clear : function()
4084     {
4085         this.getEl().dom.innerHTML = '';
4086         this.menuitems.clear();
4087     }
4088 });
4089
4090  
4091  /**
4092  * @class Roo.bootstrap.menu.Item
4093  * @extends Roo.bootstrap.Component
4094  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095  * @parent Roo.bootstrap.menu.Menu
4096  * @licence LGPL
4097  * Bootstrap MenuItem class
4098  * 
4099  * @cfg {String} html the menu label
4100  * @cfg {String} href the link
4101  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4104  * @cfg {String} fa favicon to show on left of menu item.
4105  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4106  * 
4107  * 
4108  * @constructor
4109  * Create a new MenuItem
4110  * @param {Object} config The config object
4111  */
4112
4113
4114 Roo.bootstrap.menu.Item = function(config){
4115     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4116     this.addEvents({
4117         // raw events
4118         /**
4119          * @event click
4120          * The raw click event for the entire grid.
4121          * @param {Roo.bootstrap.menu.Item} this
4122          * @param {Roo.EventObject} e
4123          */
4124         "click" : true
4125     });
4126 };
4127
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4129     
4130     href : false,
4131     html : false,
4132     preventDefault: false,
4133     isContainer : false,
4134     active : false,
4135     fa: false,
4136     
4137     getAutoCreate : function(){
4138         
4139         if(this.isContainer){
4140             return {
4141                 tag: 'li',
4142                 cls: 'dropdown-menu-item '
4143             };
4144         }
4145         var ctag = {
4146             tag: 'span',
4147             html: 'Link'
4148         };
4149         
4150         var anc = {
4151             tag : 'a',
4152             cls : 'dropdown-item',
4153             href : '#',
4154             cn : [  ]
4155         };
4156         
4157         if (this.fa !== false) {
4158             anc.cn.push({
4159                 tag : 'i',
4160                 cls : 'fa fa-' + this.fa
4161             });
4162         }
4163         
4164         anc.cn.push(ctag);
4165         
4166         
4167         var cfg= {
4168             tag: 'li',
4169             cls: 'dropdown-menu-item',
4170             cn: [ anc ]
4171         };
4172         if (this.parent().type == 'treeview') {
4173             cfg.cls = 'treeview-menu';
4174         }
4175         if (this.active) {
4176             cfg.cls += ' active';
4177         }
4178         
4179         
4180         
4181         anc.href = this.href || cfg.cn[0].href ;
4182         ctag.html = this.html || cfg.cn[0].html ;
4183         return cfg;
4184     },
4185     
4186     initEvents: function()
4187     {
4188         if (this.parent().type == 'treeview') {
4189             this.el.select('a').on('click', this.onClick, this);
4190         }
4191         
4192         if (this.menu) {
4193             this.menu.parentType = this.xtype;
4194             this.menu.triggerEl = this.el;
4195             this.menu = this.addxtype(Roo.apply({}, this.menu));
4196         }
4197         
4198     },
4199     onClick : function(e)
4200     {
4201         //Roo.log('item on click ');
4202         
4203         if(this.href === false || this.preventDefault){
4204             e.preventDefault();
4205         }
4206         //this.parent().hideMenuItems();
4207         
4208         this.fireEvent('click', this, e);
4209     },
4210     getEl : function()
4211     {
4212         return this.el;
4213     } 
4214 });
4215
4216  
4217
4218  
4219
4220   
4221 /**
4222  * @class Roo.bootstrap.menu.Separator
4223  * @extends Roo.bootstrap.Component
4224  * @licence LGPL
4225  * @parent Roo.bootstrap.menu.Menu
4226  * Bootstrap Separator class
4227  * 
4228  * @constructor
4229  * Create a new Separator
4230  * @param {Object} config The config object
4231  */
4232
4233
4234 Roo.bootstrap.menu.Separator = function(config){
4235     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 };
4237
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4239     
4240     getAutoCreate : function(){
4241         var cfg = {
4242             tag : 'li',
4243             cls: 'dropdown-divider divider'
4244         };
4245         
4246         return cfg;
4247     }
4248    
4249 });
4250
4251  
4252
4253  
4254 /*
4255 * Licence: LGPL
4256 */
4257
4258 /**
4259  * @class Roo.bootstrap.Modal
4260  * @extends Roo.bootstrap.Component
4261  * @parent none builder
4262  * @children Roo.bootstrap.Component
4263  * Bootstrap Modal class
4264  * @cfg {String} title Title of dialog
4265  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4267  * @cfg {Boolean} specificTitle default false
4268  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270  * @cfg {Boolean} animate default true
4271  * @cfg {Boolean} allow_close default true
4272  * @cfg {Boolean} fitwindow default false
4273  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276  * @cfg {String} size (sm|lg|xl) default empty
4277  * @cfg {Number} max_width set the max width of modal
4278  * @cfg {Boolean} editableTitle can the title be edited
4279
4280  *
4281  *
4282  * @constructor
4283  * Create a new Modal Dialog
4284  * @param {Object} config The config object
4285  */
4286
4287 Roo.bootstrap.Modal = function(config){
4288     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4289     this.addEvents({
4290         // raw events
4291         /**
4292          * @event btnclick
4293          * The raw btnclick event for the button
4294          * @param {Roo.EventObject} e
4295          */
4296         "btnclick" : true,
4297         /**
4298          * @event resize
4299          * Fire when dialog resize
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} e
4302          */
4303         "resize" : true,
4304         /**
4305          * @event titlechanged
4306          * Fire when the editable title has been changed
4307          * @param {Roo.bootstrap.Modal} this
4308          * @param {Roo.EventObject} value
4309          */
4310         "titlechanged" : true 
4311         
4312     });
4313     this.buttons = this.buttons || [];
4314
4315     if (this.tmpl) {
4316         this.tmpl = Roo.factory(this.tmpl);
4317     }
4318
4319 };
4320
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4322
4323     title : 'test dialog',
4324
4325     buttons : false,
4326
4327     // set on load...
4328
4329     html: false,
4330
4331     tmp: false,
4332
4333     specificTitle: false,
4334
4335     buttonPosition: 'right',
4336
4337     allow_close : true,
4338
4339     animate : true,
4340
4341     fitwindow: false,
4342     
4343      // private
4344     dialogEl: false,
4345     bodyEl:  false,
4346     footerEl:  false,
4347     titleEl:  false,
4348     closeEl:  false,
4349
4350     size: '',
4351     
4352     max_width: 0,
4353     
4354     max_height: 0,
4355     
4356     fit_content: false,
4357     editableTitle  : false,
4358
4359     onRender : function(ct, position)
4360     {
4361         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362
4363         if(!this.el){
4364             var cfg = Roo.apply({},  this.getAutoCreate());
4365             cfg.id = Roo.id();
4366             //if(!cfg.name){
4367             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368             //}
4369             //if (!cfg.name.length) {
4370             //    delete cfg.name;
4371            // }
4372             if (this.cls) {
4373                 cfg.cls += ' ' + this.cls;
4374             }
4375             if (this.style) {
4376                 cfg.style = this.style;
4377             }
4378             this.el = Roo.get(document.body).createChild(cfg, position);
4379         }
4380         //var type = this.el.dom.type;
4381
4382
4383         if(this.tabIndex !== undefined){
4384             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385         }
4386
4387         this.dialogEl = this.el.select('.modal-dialog',true).first();
4388         this.bodyEl = this.el.select('.modal-body',true).first();
4389         this.closeEl = this.el.select('.modal-header .close', true).first();
4390         this.headerEl = this.el.select('.modal-header',true).first();
4391         this.titleEl = this.el.select('.modal-title',true).first();
4392         this.footerEl = this.el.select('.modal-footer',true).first();
4393
4394         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395         
4396         //this.el.addClass("x-dlg-modal");
4397
4398         if (this.buttons.length) {
4399             Roo.each(this.buttons, function(bb) {
4400                 var b = Roo.apply({}, bb);
4401                 b.xns = b.xns || Roo.bootstrap;
4402                 b.xtype = b.xtype || 'Button';
4403                 if (typeof(b.listeners) == 'undefined') {
4404                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4405                 }
4406
4407                 var btn = Roo.factory(b);
4408
4409                 btn.render(this.getButtonContainer());
4410
4411             },this);
4412         }
4413         // render the children.
4414         var nitems = [];
4415
4416         if(typeof(this.items) != 'undefined'){
4417             var items = this.items;
4418             delete this.items;
4419
4420             for(var i =0;i < items.length;i++) {
4421                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4422             }
4423         }
4424
4425         this.items = nitems;
4426
4427         // where are these used - they used to be body/close/footer
4428
4429
4430         this.initEvents();
4431         //this.el.addClass([this.fieldClass, this.cls]);
4432
4433     },
4434
4435     getAutoCreate : function()
4436     {
4437         // we will default to modal-body-overflow - might need to remove or make optional later.
4438         var bdy = {
4439                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4440                 html : this.html || ''
4441         };
4442
4443         var title = {
4444             tag: 'h5',
4445             cls : 'modal-title',
4446             html : this.title
4447         };
4448
4449         if(this.specificTitle){ // WTF is this?
4450             title = this.title;
4451         }
4452
4453         var header = [];
4454         if (this.allow_close && Roo.bootstrap.version == 3) {
4455             header.push({
4456                 tag: 'button',
4457                 cls : 'close',
4458                 html : '&times'
4459             });
4460         }
4461
4462         header.push(title);
4463
4464         if (this.editableTitle) {
4465             header.push({
4466                 cls: 'form-control roo-editable-title d-none',
4467                 tag: 'input',
4468                 type: 'text'
4469             });
4470         }
4471         
4472         if (this.allow_close && Roo.bootstrap.version == 4) {
4473             header.push({
4474                 tag: 'button',
4475                 cls : 'close',
4476                 html : '&times'
4477             });
4478         }
4479         
4480         var size = '';
4481
4482         if(this.size.length){
4483             size = 'modal-' + this.size;
4484         }
4485         
4486         var footer = Roo.bootstrap.version == 3 ?
4487             {
4488                 cls : 'modal-footer',
4489                 cn : [
4490                     {
4491                         tag: 'div',
4492                         cls: 'btn-' + this.buttonPosition
4493                     }
4494                 ]
4495
4496             } :
4497             {  // BS4 uses mr-auto on left buttons....
4498                 cls : 'modal-footer'
4499             };
4500
4501             
4502
4503         
4504         
4505         var modal = {
4506             cls: "modal",
4507              cn : [
4508                 {
4509                     cls: "modal-dialog " + size,
4510                     cn : [
4511                         {
4512                             cls : "modal-content",
4513                             cn : [
4514                                 {
4515                                     cls : 'modal-header',
4516                                     cn : header
4517                                 },
4518                                 bdy,
4519                                 footer
4520                             ]
4521
4522                         }
4523                     ]
4524
4525                 }
4526             ]
4527         };
4528
4529         if(this.animate){
4530             modal.cls += ' fade';
4531         }
4532
4533         return modal;
4534
4535     },
4536     getChildContainer : function() {
4537
4538          return this.bodyEl;
4539
4540     },
4541     getButtonContainer : function() {
4542         
4543          return Roo.bootstrap.version == 4 ?
4544             this.el.select('.modal-footer',true).first()
4545             : this.el.select('.modal-footer div',true).first();
4546
4547     },
4548     initEvents : function()
4549     {
4550         if (this.allow_close) {
4551             this.closeEl.on('click', this.hide, this);
4552         }
4553         Roo.EventManager.onWindowResize(this.resize, this, true);
4554         if (this.editableTitle) {
4555             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4556             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557             this.headerEditEl.on('keyup', function(e) {
4558                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559                         this.toggleHeaderInput(false)
4560                     }
4561                 }, this);
4562             this.headerEditEl.on('blur', function(e) {
4563                 this.toggleHeaderInput(false)
4564             },this);
4565         }
4566
4567     },
4568   
4569
4570     resize : function()
4571     {
4572         this.maskEl.setSize(
4573             Roo.lib.Dom.getViewWidth(true),
4574             Roo.lib.Dom.getViewHeight(true)
4575         );
4576         
4577         if (this.fitwindow) {
4578             
4579            this.dialogEl.setStyle( { 'max-width' : '100%' });
4580             this.setSize(
4581                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4583             );
4584             return;
4585         }
4586         
4587         if(this.max_width !== 0) {
4588             
4589             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4590             
4591             if(this.height) {
4592                 this.setSize(w, this.height);
4593                 return;
4594             }
4595             
4596             if(this.max_height) {
4597                 this.setSize(w,Math.min(
4598                     this.max_height,
4599                     Roo.lib.Dom.getViewportHeight(true) - 60
4600                 ));
4601                 
4602                 return;
4603             }
4604             
4605             if(!this.fit_content) {
4606                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4607                 return;
4608             }
4609             
4610             this.setSize(w, Math.min(
4611                 60 +
4612                 this.headerEl.getHeight() + 
4613                 this.footerEl.getHeight() + 
4614                 this.getChildHeight(this.bodyEl.dom.childNodes),
4615                 Roo.lib.Dom.getViewportHeight(true) - 60)
4616             );
4617         }
4618         
4619     },
4620
4621     setSize : function(w,h)
4622     {
4623         if (!w && !h) {
4624             return;
4625         }
4626         
4627         this.resizeTo(w,h);
4628     },
4629
4630     show : function() {
4631
4632         if (!this.rendered) {
4633             this.render();
4634         }
4635         this.toggleHeaderInput(false);
4636         //this.el.setStyle('display', 'block');
4637         this.el.removeClass('hideing');
4638         this.el.dom.style.display='block';
4639         
4640         Roo.get(document.body).addClass('modal-open');
4641  
4642         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4643             
4644             (function(){
4645                 this.el.addClass('show');
4646                 this.el.addClass('in');
4647             }).defer(50, this);
4648         }else{
4649             this.el.addClass('show');
4650             this.el.addClass('in');
4651         }
4652
4653         // not sure how we can show data in here..
4654         //if (this.tmpl) {
4655         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4656         //}
4657
4658         Roo.get(document.body).addClass("x-body-masked");
4659         
4660         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4661         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662         this.maskEl.dom.style.display = 'block';
4663         this.maskEl.addClass('show');
4664         
4665         
4666         this.resize();
4667         
4668         this.fireEvent('show', this);
4669
4670         // set zindex here - otherwise it appears to be ignored...
4671         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672
4673         (function () {
4674             this.items.forEach( function(e) {
4675                 e.layout ? e.layout() : false;
4676
4677             });
4678         }).defer(100,this);
4679
4680     },
4681     hide : function()
4682     {
4683         if(this.fireEvent("beforehide", this) !== false){
4684             
4685             this.maskEl.removeClass('show');
4686             
4687             this.maskEl.dom.style.display = '';
4688             Roo.get(document.body).removeClass("x-body-masked");
4689             this.el.removeClass('in');
4690             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4691
4692             if(this.animate){ // why
4693                 this.el.addClass('hideing');
4694                 this.el.removeClass('show');
4695                 (function(){
4696                     if (!this.el.hasClass('hideing')) {
4697                         return; // it's been shown again...
4698                     }
4699                     
4700                     this.el.dom.style.display='';
4701
4702                     Roo.get(document.body).removeClass('modal-open');
4703                     this.el.removeClass('hideing');
4704                 }).defer(150,this);
4705                 
4706             }else{
4707                 this.el.removeClass('show');
4708                 this.el.dom.style.display='';
4709                 Roo.get(document.body).removeClass('modal-open');
4710
4711             }
4712             this.fireEvent('hide', this);
4713         }
4714     },
4715     isVisible : function()
4716     {
4717         
4718         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4719         
4720     },
4721
4722     addButton : function(str, cb)
4723     {
4724
4725
4726         var b = Roo.apply({}, { html : str } );
4727         b.xns = b.xns || Roo.bootstrap;
4728         b.xtype = b.xtype || 'Button';
4729         if (typeof(b.listeners) == 'undefined') {
4730             b.listeners = { click : cb.createDelegate(this)  };
4731         }
4732
4733         var btn = Roo.factory(b);
4734
4735         btn.render(this.getButtonContainer());
4736
4737         return btn;
4738
4739     },
4740
4741     setDefaultButton : function(btn)
4742     {
4743         //this.el.select('.modal-footer').()
4744     },
4745
4746     resizeTo: function(w,h)
4747     {
4748         this.dialogEl.setWidth(w);
4749         
4750         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4751
4752         this.bodyEl.setHeight(h - diff);
4753         
4754         this.fireEvent('resize', this);
4755     },
4756     
4757     setContentSize  : function(w, h)
4758     {
4759
4760     },
4761     onButtonClick: function(btn,e)
4762     {
4763         //Roo.log([a,b,c]);
4764         this.fireEvent('btnclick', btn.name, e);
4765     },
4766      /**
4767      * Set the title of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setTitle: function(str) {
4771         this.titleEl.dom.innerHTML = str;
4772         this.title = str;
4773     },
4774     /**
4775      * Set the body of the Dialog
4776      * @param {String} str new Title
4777      */
4778     setBody: function(str) {
4779         this.bodyEl.dom.innerHTML = str;
4780     },
4781     /**
4782      * Set the body of the Dialog using the template
4783      * @param {Obj} data - apply this data to the template and replace the body contents.
4784      */
4785     applyBody: function(obj)
4786     {
4787         if (!this.tmpl) {
4788             Roo.log("Error - using apply Body without a template");
4789             //code
4790         }
4791         this.tmpl.overwrite(this.bodyEl, obj);
4792     },
4793     
4794     getChildHeight : function(child_nodes)
4795     {
4796         if(
4797             !child_nodes ||
4798             child_nodes.length == 0
4799         ) {
4800             return 0;
4801         }
4802         
4803         var child_height = 0;
4804         
4805         for(var i = 0; i < child_nodes.length; i++) {
4806             
4807             /*
4808             * for modal with tabs...
4809             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4810                 
4811                 var layout_childs = child_nodes[i].childNodes;
4812                 
4813                 for(var j = 0; j < layout_childs.length; j++) {
4814                     
4815                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4816                         
4817                         var layout_body_childs = layout_childs[j].childNodes;
4818                         
4819                         for(var k = 0; k < layout_body_childs.length; k++) {
4820                             
4821                             if(layout_body_childs[k].classList.contains('navbar')) {
4822                                 child_height += layout_body_childs[k].offsetHeight;
4823                                 continue;
4824                             }
4825                             
4826                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4827                                 
4828                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4829                                 
4830                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4831                                     
4832                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4834                                         continue;
4835                                     }
4836                                     
4837                                 }
4838                                 
4839                             }
4840                             
4841                         }
4842                     }
4843                 }
4844                 continue;
4845             }
4846             */
4847             
4848             child_height += child_nodes[i].offsetHeight;
4849             // Roo.log(child_nodes[i].offsetHeight);
4850         }
4851         
4852         return child_height;
4853     },
4854     toggleHeaderInput : function(is_edit)
4855     {
4856         if (!this.editableTitle) {
4857             return; // not editable.
4858         }
4859         if (is_edit && this.is_header_editing) {
4860             return; // already editing..
4861         }
4862         if (is_edit) {
4863     
4864             this.headerEditEl.dom.value = this.title;
4865             this.headerEditEl.removeClass('d-none');
4866             this.headerEditEl.dom.focus();
4867             this.titleEl.addClass('d-none');
4868             
4869             this.is_header_editing = true;
4870             return
4871         }
4872         // flip back to not editing.
4873         this.title = this.headerEditEl.dom.value;
4874         this.headerEditEl.addClass('d-none');
4875         this.titleEl.removeClass('d-none');
4876         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877         this.is_header_editing = false;
4878         this.fireEvent('titlechanged', this, this.title);
4879     
4880             
4881         
4882     }
4883
4884 });
4885
4886
4887 Roo.apply(Roo.bootstrap.Modal,  {
4888     /**
4889          * Button config that displays a single OK button
4890          * @type Object
4891          */
4892         OK :  [{
4893             name : 'ok',
4894             weight : 'primary',
4895             html : 'OK'
4896         }],
4897         /**
4898          * Button config that displays Yes and No buttons
4899          * @type Object
4900          */
4901         YESNO : [
4902             {
4903                 name  : 'no',
4904                 html : 'No'
4905             },
4906             {
4907                 name  :'yes',
4908                 weight : 'primary',
4909                 html : 'Yes'
4910             }
4911         ],
4912
4913         /**
4914          * Button config that displays OK and Cancel buttons
4915          * @type Object
4916          */
4917         OKCANCEL : [
4918             {
4919                name : 'cancel',
4920                 html : 'Cancel'
4921             },
4922             {
4923                 name : 'ok',
4924                 weight : 'primary',
4925                 html : 'OK'
4926             }
4927         ],
4928         /**
4929          * Button config that displays Yes, No and Cancel buttons
4930          * @type Object
4931          */
4932         YESNOCANCEL : [
4933             {
4934                 name : 'yes',
4935                 weight : 'primary',
4936                 html : 'Yes'
4937             },
4938             {
4939                 name : 'no',
4940                 html : 'No'
4941             },
4942             {
4943                 name : 'cancel',
4944                 html : 'Cancel'
4945             }
4946         ],
4947         
4948         zIndex : 10001
4949 });
4950
4951 /*
4952  * - LGPL
4953  *
4954  * messagebox - can be used as a replace
4955  * 
4956  */
4957 /**
4958  * @class Roo.MessageBox
4959  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4960  * Example usage:
4961  *<pre><code>
4962 // Basic alert:
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4964
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4967     if (btn == 'ok'){
4968         // process text value...
4969     }
4970 });
4971
4972 // Show a dialog using config options:
4973 Roo.Msg.show({
4974    title:'Save Changes?',
4975    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976    buttons: Roo.Msg.YESNOCANCEL,
4977    fn: processResult,
4978    animEl: 'elId'
4979 });
4980 </code></pre>
4981  * @static
4982  */
4983 Roo.bootstrap.MessageBox = function(){
4984     var dlg, opt, mask, waitTimer;
4985     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986     var buttons, activeTextEl, bwidth;
4987
4988     
4989     // private
4990     var handleButton = function(button){
4991         dlg.hide();
4992         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4993     };
4994
4995     // private
4996     var handleHide = function(){
4997         if(opt && opt.cls){
4998             dlg.el.removeClass(opt.cls);
4999         }
5000         //if(waitTimer){
5001         //    Roo.TaskMgr.stop(waitTimer);
5002         //    waitTimer = null;
5003         //}
5004     };
5005
5006     // private
5007     var updateButtons = function(b){
5008         var width = 0;
5009         if(!b){
5010             buttons["ok"].hide();
5011             buttons["cancel"].hide();
5012             buttons["yes"].hide();
5013             buttons["no"].hide();
5014             dlg.footerEl.hide();
5015             
5016             return width;
5017         }
5018         dlg.footerEl.show();
5019         for(var k in buttons){
5020             if(typeof buttons[k] != "function"){
5021                 if(b[k]){
5022                     buttons[k].show();
5023                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024                     width += buttons[k].el.getWidth()+15;
5025                 }else{
5026                     buttons[k].hide();
5027                 }
5028             }
5029         }
5030         return width;
5031     };
5032
5033     // private
5034     var handleEsc = function(d, k, e){
5035         if(opt && opt.closable !== false){
5036             dlg.hide();
5037         }
5038         if(e){
5039             e.stopEvent();
5040         }
5041     };
5042
5043     return {
5044         /**
5045          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046          * @return {Roo.BasicDialog} The BasicDialog element
5047          */
5048         getDialog : function(){
5049            if(!dlg){
5050                 dlg = new Roo.bootstrap.Modal( {
5051                     //draggable: true,
5052                     //resizable:false,
5053                     //constraintoviewport:false,
5054                     //fixedcenter:true,
5055                     //collapsible : false,
5056                     //shim:true,
5057                     //modal: true,
5058                 //    width: 'auto',
5059                   //  height:100,
5060                     //buttonAlign:"center",
5061                     closeClick : function(){
5062                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5063                             handleButton("no");
5064                         }else{
5065                             handleButton("cancel");
5066                         }
5067                     }
5068                 });
5069                 dlg.render();
5070                 dlg.on("hide", handleHide);
5071                 mask = dlg.mask;
5072                 //dlg.addKeyListener(27, handleEsc);
5073                 buttons = {};
5074                 this.buttons = buttons;
5075                 var bt = this.buttonText;
5076                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5080                 //Roo.log(buttons);
5081                 bodyEl = dlg.bodyEl.createChild({
5082
5083                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084                         '<textarea class="roo-mb-textarea"></textarea>' +
5085                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5086                 });
5087                 msgEl = bodyEl.dom.firstChild;
5088                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089                 textboxEl.enableDisplayMode();
5090                 textboxEl.addKeyListener([10,13], function(){
5091                     if(dlg.isVisible() && opt && opt.buttons){
5092                         if(opt.buttons.ok){
5093                             handleButton("ok");
5094                         }else if(opt.buttons.yes){
5095                             handleButton("yes");
5096                         }
5097                     }
5098                 });
5099                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100                 textareaEl.enableDisplayMode();
5101                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102                 progressEl.enableDisplayMode();
5103                 
5104                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105                 var pf = progressEl.dom.firstChild;
5106                 if (pf) {
5107                     pp = Roo.get(pf.firstChild);
5108                     pp.setHeight(pf.offsetHeight);
5109                 }
5110                 
5111             }
5112             return dlg;
5113         },
5114
5115         /**
5116          * Updates the message box body text
5117          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118          * the XHTML-compliant non-breaking space character '&amp;#160;')
5119          * @return {Roo.MessageBox} This message box
5120          */
5121         updateText : function(text)
5122         {
5123             if(!dlg.isVisible() && !opt.width){
5124                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5126             }
5127             msgEl.innerHTML = text || '&#160;';
5128       
5129             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5131             var w = Math.max(
5132                     Math.min(opt.width || cw , this.maxWidth), 
5133                     Math.max(opt.minWidth || this.minWidth, bwidth)
5134             );
5135             if(opt.prompt){
5136                 activeTextEl.setWidth(w);
5137             }
5138             if(dlg.isVisible()){
5139                 dlg.fixedcenter = false;
5140             }
5141             // to big, make it scroll. = But as usual stupid IE does not support
5142             // !important..
5143             
5144             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5147             } else {
5148                 bodyEl.dom.style.height = '';
5149                 bodyEl.dom.style.overflowY = '';
5150             }
5151             if (cw > w) {
5152                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5153             } else {
5154                 bodyEl.dom.style.overflowX = '';
5155             }
5156             
5157             dlg.setContentSize(w, bodyEl.getHeight());
5158             if(dlg.isVisible()){
5159                 dlg.fixedcenter = true;
5160             }
5161             return this;
5162         },
5163
5164         /**
5165          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5166          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169          * @return {Roo.MessageBox} This message box
5170          */
5171         updateProgress : function(value, text){
5172             if(text){
5173                 this.updateText(text);
5174             }
5175             
5176             if (pp) { // weird bug on my firefox - for some reason this is not defined
5177                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5179             }
5180             return this;
5181         },        
5182
5183         /**
5184          * Returns true if the message box is currently displayed
5185          * @return {Boolean} True if the message box is visible, else false
5186          */
5187         isVisible : function(){
5188             return dlg && dlg.isVisible();  
5189         },
5190
5191         /**
5192          * Hides the message box if it is displayed
5193          */
5194         hide : function(){
5195             if(this.isVisible()){
5196                 dlg.hide();
5197             }  
5198         },
5199
5200         /**
5201          * Displays a new message box, or reinitializes an existing message box, based on the config options
5202          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203          * The following config object properties are supported:
5204          * <pre>
5205 Property    Type             Description
5206 ----------  ---------------  ------------------------------------------------------------------------------------
5207 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5208                                    closes (defaults to undefined)
5209 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5212                                    progress and wait dialogs will ignore this property and always hide the
5213                                    close button as they can only be closed programmatically.
5214 cls               String           A custom CSS class to apply to the message box element
5215 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5216                                    displayed (defaults to 75)
5217 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5218                                    function will be btn (the name of the button that was clicked, if applicable,
5219                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5220                                    Progress and wait dialogs will ignore this option since they do not respond to
5221                                    user actions and can only be closed programmatically, so any required function
5222                                    should be called by the same code after it closes the dialog.
5223 icon              String           A CSS class that provides a background image to be used as an icon for
5224                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5226 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5227 modal             Boolean          False to allow user interaction with the page while the message box is
5228                                    displayed (defaults to true)
5229 msg               String           A string that will replace the existing message box body text (defaults
5230                                    to the XHTML-compliant non-breaking space character '&#160;')
5231 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5232 progress          Boolean          True to display a progress bar (defaults to false)
5233 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5236 title             String           The title text
5237 value             String           The string value to set into the active textbox element if displayed
5238 wait              Boolean          True to display a progress bar (defaults to false)
5239 width             Number           The width of the dialog in pixels
5240 </pre>
5241          *
5242          * Example usage:
5243          * <pre><code>
5244 Roo.Msg.show({
5245    title: 'Address',
5246    msg: 'Please enter your address:',
5247    width: 300,
5248    buttons: Roo.MessageBox.OKCANCEL,
5249    multiline: true,
5250    fn: saveAddress,
5251    animEl: 'addAddressBtn'
5252 });
5253 </code></pre>
5254          * @param {Object} config Configuration options
5255          * @return {Roo.MessageBox} This message box
5256          */
5257         show : function(options)
5258         {
5259             
5260             // this causes nightmares if you show one dialog after another
5261             // especially on callbacks..
5262              
5263             if(this.isVisible()){
5264                 
5265                 this.hide();
5266                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5268                 Roo.log("New Dialog Message:" +  options.msg )
5269                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5271                 
5272             }
5273             var d = this.getDialog();
5274             opt = options;
5275             d.setTitle(opt.title || "&#160;");
5276             d.closeEl.setDisplayed(opt.closable !== false);
5277             activeTextEl = textboxEl;
5278             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5279             if(opt.prompt){
5280                 if(opt.multiline){
5281                     textboxEl.hide();
5282                     textareaEl.show();
5283                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5284                         opt.multiline : this.defaultTextHeight);
5285                     activeTextEl = textareaEl;
5286                 }else{
5287                     textboxEl.show();
5288                     textareaEl.hide();
5289                 }
5290             }else{
5291                 textboxEl.hide();
5292                 textareaEl.hide();
5293             }
5294             progressEl.setDisplayed(opt.progress === true);
5295             if (opt.progress) {
5296                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5297             }
5298             this.updateProgress(0);
5299             activeTextEl.dom.value = opt.value || "";
5300             if(opt.prompt){
5301                 dlg.setDefaultButton(activeTextEl);
5302             }else{
5303                 var bs = opt.buttons;
5304                 var db = null;
5305                 if(bs && bs.ok){
5306                     db = buttons["ok"];
5307                 }else if(bs && bs.yes){
5308                     db = buttons["yes"];
5309                 }
5310                 dlg.setDefaultButton(db);
5311             }
5312             bwidth = updateButtons(opt.buttons);
5313             this.updateText(opt.msg);
5314             if(opt.cls){
5315                 d.el.addClass(opt.cls);
5316             }
5317             d.proxyDrag = opt.proxyDrag === true;
5318             d.modal = opt.modal !== false;
5319             d.mask = opt.modal !== false ? mask : false;
5320             if(!d.isVisible()){
5321                 // force it to the end of the z-index stack so it gets a cursor in FF
5322                 document.body.appendChild(dlg.el.dom);
5323                 d.animateTarget = null;
5324                 d.show(options.animEl);
5325             }
5326             return this;
5327         },
5328
5329         /**
5330          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5331          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332          * and closing the message box when the process is complete.
5333          * @param {String} title The title bar text
5334          * @param {String} msg The message box body text
5335          * @return {Roo.MessageBox} This message box
5336          */
5337         progress : function(title, msg){
5338             this.show({
5339                 title : title,
5340                 msg : msg,
5341                 buttons: false,
5342                 progress:true,
5343                 closable:false,
5344                 minWidth: this.minProgressWidth,
5345                 modal : true
5346             });
5347             return this;
5348         },
5349
5350         /**
5351          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352          * If a callback function is passed it will be called after the user clicks the button, and the
5353          * id of the button that was clicked will be passed as the only parameter to the callback
5354          * (could also be the top-right close button).
5355          * @param {String} title The title bar text
5356          * @param {String} msg The message box body text
5357          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358          * @param {Object} scope (optional) The scope of the callback function
5359          * @return {Roo.MessageBox} This message box
5360          */
5361         alert : function(title, msg, fn, scope)
5362         {
5363             this.show({
5364                 title : title,
5365                 msg : msg,
5366                 buttons: this.OK,
5367                 fn: fn,
5368                 closable : false,
5369                 scope : scope,
5370                 modal : true
5371             });
5372             return this;
5373         },
5374
5375         /**
5376          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5377          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378          * You are responsible for closing the message box when the process is complete.
5379          * @param {String} msg The message box body text
5380          * @param {String} title (optional) The title bar text
5381          * @return {Roo.MessageBox} This message box
5382          */
5383         wait : function(msg, title){
5384             this.show({
5385                 title : title,
5386                 msg : msg,
5387                 buttons: false,
5388                 closable:false,
5389                 progress:true,
5390                 modal:true,
5391                 width:300,
5392                 wait:true
5393             });
5394             waitTimer = Roo.TaskMgr.start({
5395                 run: function(i){
5396                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5397                 },
5398                 interval: 1000
5399             });
5400             return this;
5401         },
5402
5403         /**
5404          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407          * @param {String} title The title bar text
5408          * @param {String} msg The message box body text
5409          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410          * @param {Object} scope (optional) The scope of the callback function
5411          * @return {Roo.MessageBox} This message box
5412          */
5413         confirm : function(title, msg, fn, scope){
5414             this.show({
5415                 title : title,
5416                 msg : msg,
5417                 buttons: this.YESNO,
5418                 fn: fn,
5419                 scope : scope,
5420                 modal : true
5421             });
5422             return this;
5423         },
5424
5425         /**
5426          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5428          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429          * (could also be the top-right close button) and the text that was entered will be passed as the two
5430          * parameters to the callback.
5431          * @param {String} title The title bar text
5432          * @param {String} msg The message box body text
5433          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434          * @param {Object} scope (optional) The scope of the callback function
5435          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437          * @return {Roo.MessageBox} This message box
5438          */
5439         prompt : function(title, msg, fn, scope, multiline){
5440             this.show({
5441                 title : title,
5442                 msg : msg,
5443                 buttons: this.OKCANCEL,
5444                 fn: fn,
5445                 minWidth:250,
5446                 scope : scope,
5447                 prompt:true,
5448                 multiline: multiline,
5449                 modal : true
5450             });
5451             return this;
5452         },
5453
5454         /**
5455          * Button config that displays a single OK button
5456          * @type Object
5457          */
5458         OK : {ok:true},
5459         /**
5460          * Button config that displays Yes and No buttons
5461          * @type Object
5462          */
5463         YESNO : {yes:true, no:true},
5464         /**
5465          * Button config that displays OK and Cancel buttons
5466          * @type Object
5467          */
5468         OKCANCEL : {ok:true, cancel:true},
5469         /**
5470          * Button config that displays Yes, No and Cancel buttons
5471          * @type Object
5472          */
5473         YESNOCANCEL : {yes:true, no:true, cancel:true},
5474
5475         /**
5476          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5477          * @type Number
5478          */
5479         defaultTextHeight : 75,
5480         /**
5481          * The maximum width in pixels of the message box (defaults to 600)
5482          * @type Number
5483          */
5484         maxWidth : 600,
5485         /**
5486          * The minimum width in pixels of the message box (defaults to 100)
5487          * @type Number
5488          */
5489         minWidth : 100,
5490         /**
5491          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5492          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5493          * @type Number
5494          */
5495         minProgressWidth : 250,
5496         /**
5497          * An object containing the default button text strings that can be overriden for localized language support.
5498          * Supported properties are: ok, cancel, yes and no.
5499          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5500          * @type Object
5501          */
5502         buttonText : {
5503             ok : "OK",
5504             cancel : "Cancel",
5505             yes : "Yes",
5506             no : "No"
5507         }
5508     };
5509 }();
5510
5511 /**
5512  * Shorthand for {@link Roo.MessageBox}
5513  */
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 /*
5517  * - LGPL
5518  *
5519  * navbar
5520  * 
5521  */
5522
5523 /**
5524  * @class Roo.bootstrap.nav.Bar
5525  * @extends Roo.bootstrap.Component
5526  * @abstract
5527  * Bootstrap Navbar class
5528
5529  * @constructor
5530  * Create a new Navbar
5531  * @param {Object} config The config object
5532  */
5533
5534
5535 Roo.bootstrap.nav.Bar = function(config){
5536     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5537     this.addEvents({
5538         // raw events
5539         /**
5540          * @event beforetoggle
5541          * Fire before toggle the menu
5542          * @param {Roo.EventObject} e
5543          */
5544         "beforetoggle" : true
5545     });
5546 };
5547
5548 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5549     
5550     
5551    
5552     // private
5553     navItems : false,
5554     loadMask : false,
5555     
5556     
5557     getAutoCreate : function(){
5558         
5559         
5560         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5561         
5562     },
5563     
5564     initEvents :function ()
5565     {
5566         //Roo.log(this.el.select('.navbar-toggle',true));
5567         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5568         
5569         var mark = {
5570             tag: "div",
5571             cls:"x-dlg-mask"
5572         };
5573         
5574         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5575         
5576         var size = this.el.getSize();
5577         this.maskEl.setSize(size.width, size.height);
5578         this.maskEl.enableDisplayMode("block");
5579         this.maskEl.hide();
5580         
5581         if(this.loadMask){
5582             this.maskEl.show();
5583         }
5584     },
5585     
5586     
5587     getChildContainer : function()
5588     {
5589         if (this.el && this.el.select('.collapse').getCount()) {
5590             return this.el.select('.collapse',true).first();
5591         }
5592         
5593         return this.el;
5594     },
5595     
5596     mask : function()
5597     {
5598         this.maskEl.show();
5599     },
5600     
5601     unmask : function()
5602     {
5603         this.maskEl.hide();
5604     },
5605     onToggle : function()
5606     {
5607         
5608         if(this.fireEvent('beforetoggle', this) === false){
5609             return;
5610         }
5611         var ce = this.el.select('.navbar-collapse',true).first();
5612       
5613         if (!ce.hasClass('show')) {
5614            this.expand();
5615         } else {
5616             this.collapse();
5617         }
5618         
5619         
5620     
5621     },
5622     /**
5623      * Expand the navbar pulldown 
5624      */
5625     expand : function ()
5626     {
5627        
5628         var ce = this.el.select('.navbar-collapse',true).first();
5629         if (ce.hasClass('collapsing')) {
5630             return;
5631         }
5632         ce.dom.style.height = '';
5633                // show it...
5634         ce.addClass('in'); // old...
5635         ce.removeClass('collapse');
5636         ce.addClass('show');
5637         var h = ce.getHeight();
5638         Roo.log(h);
5639         ce.removeClass('show');
5640         // at this point we should be able to see it..
5641         ce.addClass('collapsing');
5642         
5643         ce.setHeight(0); // resize it ...
5644         ce.on('transitionend', function() {
5645             //Roo.log('done transition');
5646             ce.removeClass('collapsing');
5647             ce.addClass('show');
5648             ce.removeClass('collapse');
5649
5650             ce.dom.style.height = '';
5651         }, this, { single: true} );
5652         ce.setHeight(h);
5653         ce.dom.scrollTop = 0;
5654     },
5655     /**
5656      * Collapse the navbar pulldown 
5657      */
5658     collapse : function()
5659     {
5660          var ce = this.el.select('.navbar-collapse',true).first();
5661        
5662         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5663             // it's collapsed or collapsing..
5664             return;
5665         }
5666         ce.removeClass('in'); // old...
5667         ce.setHeight(ce.getHeight());
5668         ce.removeClass('show');
5669         ce.addClass('collapsing');
5670         
5671         ce.on('transitionend', function() {
5672             ce.dom.style.height = '';
5673             ce.removeClass('collapsing');
5674             ce.addClass('collapse');
5675         }, this, { single: true} );
5676         ce.setHeight(0);
5677     }
5678     
5679     
5680     
5681 });
5682
5683
5684
5685  
5686
5687  /*
5688  * - LGPL
5689  *
5690  * navbar
5691  * 
5692  */
5693
5694 /**
5695  * @class Roo.bootstrap.nav.Simplebar
5696  * @extends Roo.bootstrap.nav.Bar
5697  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5698  * Bootstrap Sidebar class
5699  *
5700  * @cfg {Boolean} inverse is inverted color
5701  * 
5702  * @cfg {String} type (nav | pills | tabs)
5703  * @cfg {Boolean} arrangement stacked | justified
5704  * @cfg {String} align (left | right) alignment
5705  * 
5706  * @cfg {Boolean} main (true|false) main nav bar? default false
5707  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5708  * 
5709  * @cfg {String} tag (header|footer|nav|div) default is nav 
5710
5711  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5712  * 
5713  * 
5714  * @constructor
5715  * Create a new Sidebar
5716  * @param {Object} config The config object
5717  */
5718
5719
5720 Roo.bootstrap.nav.Simplebar = function(config){
5721     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5722 };
5723
5724 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5725     
5726     inverse: false,
5727     
5728     type: false,
5729     arrangement: '',
5730     align : false,
5731     
5732     weight : 'light',
5733     
5734     main : false,
5735     
5736     
5737     tag : false,
5738     
5739     
5740     getAutoCreate : function(){
5741         
5742         
5743         var cfg = {
5744             tag : this.tag || 'div',
5745             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5746         };
5747         if (['light','white'].indexOf(this.weight) > -1) {
5748             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5749         }
5750         cfg.cls += ' bg-' + this.weight;
5751         
5752         if (this.inverse) {
5753             cfg.cls += ' navbar-inverse';
5754             
5755         }
5756         
5757         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5758         
5759         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5760             return cfg;
5761         }
5762         
5763         
5764     
5765         
5766         cfg.cn = [
5767             {
5768                 cls: 'nav nav-' + this.xtype,
5769                 tag : 'ul'
5770             }
5771         ];
5772         
5773          
5774         this.type = this.type || 'nav';
5775         if (['tabs','pills'].indexOf(this.type) != -1) {
5776             cfg.cn[0].cls += ' nav-' + this.type
5777         
5778         
5779         } else {
5780             if (this.type!=='nav') {
5781                 Roo.log('nav type must be nav/tabs/pills')
5782             }
5783             cfg.cn[0].cls += ' navbar-nav'
5784         }
5785         
5786         
5787         
5788         
5789         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790             cfg.cn[0].cls += ' nav-' + this.arrangement;
5791         }
5792         
5793         
5794         if (this.align === 'right') {
5795             cfg.cn[0].cls += ' navbar-right';
5796         }
5797         
5798         
5799         
5800         
5801         return cfg;
5802     
5803         
5804     }
5805     
5806     
5807     
5808 });
5809
5810
5811
5812  
5813
5814  
5815        /*
5816  * - LGPL
5817  *
5818  * navbar
5819  * navbar-fixed-top
5820  * navbar-expand-md  fixed-top 
5821  */
5822
5823 /**
5824  * @class Roo.bootstrap.nav.Headerbar
5825  * @extends Roo.bootstrap.nav.Simplebar
5826  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5827  * Bootstrap Sidebar class
5828  *
5829  * @cfg {String} brand what is brand
5830  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5831  * @cfg {String} brand_href href of the brand
5832  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5833  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5834  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5835  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5836  * 
5837  * @constructor
5838  * Create a new Sidebar
5839  * @param {Object} config The config object
5840  */
5841
5842
5843 Roo.bootstrap.nav.Headerbar = function(config){
5844     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5845       
5846 };
5847
5848 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5849     
5850     position: '',
5851     brand: '',
5852     brand_href: false,
5853     srButton : true,
5854     autohide : false,
5855     desktopCenter : false,
5856    
5857     
5858     getAutoCreate : function(){
5859         
5860         var   cfg = {
5861             tag: this.nav || 'nav',
5862             cls: 'navbar navbar-expand-md',
5863             role: 'navigation',
5864             cn: []
5865         };
5866         
5867         var cn = cfg.cn;
5868         if (this.desktopCenter) {
5869             cn.push({cls : 'container', cn : []});
5870             cn = cn[0].cn;
5871         }
5872         
5873         if(this.srButton){
5874             var btn = {
5875                 tag: 'button',
5876                 type: 'button',
5877                 cls: 'navbar-toggle navbar-toggler',
5878                 'data-toggle': 'collapse',
5879                 cn: [
5880                     {
5881                         tag: 'span',
5882                         cls: 'sr-only',
5883                         html: 'Toggle navigation'
5884                     },
5885                     {
5886                         tag: 'span',
5887                         cls: 'icon-bar navbar-toggler-icon'
5888                     },
5889                     {
5890                         tag: 'span',
5891                         cls: 'icon-bar'
5892                     },
5893                     {
5894                         tag: 'span',
5895                         cls: 'icon-bar'
5896                     }
5897                 ]
5898             };
5899             
5900             cn.push( Roo.bootstrap.version == 4 ? btn : {
5901                 tag: 'div',
5902                 cls: 'navbar-header',
5903                 cn: [
5904                     btn
5905                 ]
5906             });
5907         }
5908         
5909         cn.push({
5910             tag: 'div',
5911             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5912             cn : []
5913         });
5914         
5915         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5916         
5917         if (['light','white'].indexOf(this.weight) > -1) {
5918             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5919         }
5920         cfg.cls += ' bg-' + this.weight;
5921         
5922         
5923         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5924             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5925             
5926             // tag can override this..
5927             
5928             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5929         }
5930         
5931         if (this.brand !== '') {
5932             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5933             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5934                 tag: 'a',
5935                 href: this.brand_href ? this.brand_href : '#',
5936                 cls: 'navbar-brand',
5937                 cn: [
5938                 this.brand
5939                 ]
5940             });
5941         }
5942         
5943         if(this.main){
5944             cfg.cls += ' main-nav';
5945         }
5946         
5947         
5948         return cfg;
5949
5950         
5951     },
5952     getHeaderChildContainer : function()
5953     {
5954         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5955             return this.el.select('.navbar-header',true).first();
5956         }
5957         
5958         return this.getChildContainer();
5959     },
5960     
5961     getChildContainer : function()
5962     {
5963          
5964         return this.el.select('.roo-navbar-collapse',true).first();
5965          
5966         
5967     },
5968     
5969     initEvents : function()
5970     {
5971         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5972         
5973         if (this.autohide) {
5974             
5975             var prevScroll = 0;
5976             var ft = this.el;
5977             
5978             Roo.get(document).on('scroll',function(e) {
5979                 var ns = Roo.get(document).getScroll().top;
5980                 var os = prevScroll;
5981                 prevScroll = ns;
5982                 
5983                 if(ns > os){
5984                     ft.removeClass('slideDown');
5985                     ft.addClass('slideUp');
5986                     return;
5987                 }
5988                 ft.removeClass('slideUp');
5989                 ft.addClass('slideDown');
5990                  
5991               
5992           },this);
5993         }
5994     }    
5995     
5996 });
5997
5998
5999
6000  
6001
6002  /*
6003  * - LGPL
6004  *
6005  * navbar
6006  * 
6007  */
6008
6009 /**
6010  * @class Roo.bootstrap.nav.Sidebar
6011  * @extends Roo.bootstrap.nav.Bar
6012  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6013  * Bootstrap Sidebar class
6014  * 
6015  * @constructor
6016  * Create a new Sidebar
6017  * @param {Object} config The config object
6018  */
6019
6020
6021 Roo.bootstrap.nav.Sidebar = function(config){
6022     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6023 };
6024
6025 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6026     
6027     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6028     
6029     getAutoCreate : function(){
6030         
6031         
6032         return  {
6033             tag: 'div',
6034             cls: 'sidebar sidebar-nav'
6035         };
6036     
6037         
6038     }
6039     
6040     
6041     
6042 });
6043
6044
6045
6046  
6047
6048  /*
6049  * - LGPL
6050  *
6051  * nav group
6052  * 
6053  */
6054
6055 /**
6056  * @class Roo.bootstrap.nav.Group
6057  * @extends Roo.bootstrap.Component
6058  * @children Roo.bootstrap.nav.Item
6059  * Bootstrap NavGroup class
6060  * @cfg {String} align (left|right)
6061  * @cfg {Boolean} inverse
6062  * @cfg {String} type (nav|pills|tab) default nav
6063  * @cfg {String} navId - reference Id for navbar.
6064  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6065  * 
6066  * @constructor
6067  * Create a new nav group
6068  * @param {Object} config The config object
6069  */
6070
6071 Roo.bootstrap.nav.Group = function(config){
6072     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6073     this.navItems = [];
6074    
6075     Roo.bootstrap.nav.Group.register(this);
6076      this.addEvents({
6077         /**
6078              * @event changed
6079              * Fires when the active item changes
6080              * @param {Roo.bootstrap.nav.Group} this
6081              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6083          */
6084         'changed': true
6085      });
6086     
6087 };
6088
6089 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6090     
6091     align: '',
6092     inverse: false,
6093     form: false,
6094     type: 'nav',
6095     navId : '',
6096     // private
6097     pilltype : true,
6098     
6099     navItems : false, 
6100     
6101     getAutoCreate : function()
6102     {
6103         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6104         
6105         cfg = {
6106             tag : 'ul',
6107             cls: 'nav' 
6108         };
6109         if (Roo.bootstrap.version == 4) {
6110             if (['tabs','pills'].indexOf(this.type) != -1) {
6111                 cfg.cls += ' nav-' + this.type; 
6112             } else {
6113                 // trying to remove so header bar can right align top?
6114                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115                     // do not use on header bar... 
6116                     cfg.cls += ' navbar-nav';
6117                 }
6118             }
6119             
6120         } else {
6121             if (['tabs','pills'].indexOf(this.type) != -1) {
6122                 cfg.cls += ' nav-' + this.type
6123             } else {
6124                 if (this.type !== 'nav') {
6125                     Roo.log('nav type must be nav/tabs/pills')
6126                 }
6127                 cfg.cls += ' navbar-nav'
6128             }
6129         }
6130         
6131         if (this.parent() && this.parent().sidebar) {
6132             cfg = {
6133                 tag: 'ul',
6134                 cls: 'dashboard-menu sidebar-menu'
6135             };
6136             
6137             return cfg;
6138         }
6139         
6140         if (this.form === true) {
6141             cfg = {
6142                 tag: 'form',
6143                 cls: 'navbar-form form-inline'
6144             };
6145             //nav navbar-right ml-md-auto
6146             if (this.align === 'right') {
6147                 cfg.cls += ' navbar-right ml-md-auto';
6148             } else {
6149                 cfg.cls += ' navbar-left';
6150             }
6151         }
6152         
6153         if (this.align === 'right') {
6154             cfg.cls += ' navbar-right ml-md-auto';
6155         } else {
6156             cfg.cls += ' mr-auto';
6157         }
6158         
6159         if (this.inverse) {
6160             cfg.cls += ' navbar-inverse';
6161             
6162         }
6163         
6164         
6165         return cfg;
6166     },
6167     /**
6168     * sets the active Navigation item
6169     * @param {Roo.bootstrap.nav.Item} the new current navitem
6170     */
6171     setActiveItem : function(item)
6172     {
6173         var prev = false;
6174         Roo.each(this.navItems, function(v){
6175             if (v == item) {
6176                 return ;
6177             }
6178             if (v.isActive()) {
6179                 v.setActive(false, true);
6180                 prev = v;
6181                 
6182             }
6183             
6184         });
6185
6186         item.setActive(true, true);
6187         this.fireEvent('changed', this, item, prev);
6188         
6189         
6190     },
6191     /**
6192     * gets the active Navigation item
6193     * @return {Roo.bootstrap.nav.Item} the current navitem
6194     */
6195     getActive : function()
6196     {
6197         
6198         var prev = false;
6199         Roo.each(this.navItems, function(v){
6200             
6201             if (v.isActive()) {
6202                 prev = v;
6203                 
6204             }
6205             
6206         });
6207         return prev;
6208     },
6209     
6210     indexOfNav : function()
6211     {
6212         
6213         var prev = false;
6214         Roo.each(this.navItems, function(v,i){
6215             
6216             if (v.isActive()) {
6217                 prev = i;
6218                 
6219             }
6220             
6221         });
6222         return prev;
6223     },
6224     /**
6225     * adds a Navigation item
6226     * @param {Roo.bootstrap.nav.Item} the navitem to add
6227     */
6228     addItem : function(cfg)
6229     {
6230         if (this.form && Roo.bootstrap.version == 4) {
6231             cfg.tag = 'div';
6232         }
6233         var cn = new Roo.bootstrap.nav.Item(cfg);
6234         this.register(cn);
6235         cn.parentId = this.id;
6236         cn.onRender(this.el, null);
6237         return cn;
6238     },
6239     /**
6240     * register a Navigation item
6241     * @param {Roo.bootstrap.nav.Item} the navitem to add
6242     */
6243     register : function(item)
6244     {
6245         this.navItems.push( item);
6246         item.navId = this.navId;
6247     
6248     },
6249     
6250     /**
6251     * clear all the Navigation item
6252     */
6253    
6254     clearAll : function()
6255     {
6256         this.navItems = [];
6257         this.el.dom.innerHTML = '';
6258     },
6259     
6260     getNavItem: function(tabId)
6261     {
6262         var ret = false;
6263         Roo.each(this.navItems, function(e) {
6264             if (e.tabId == tabId) {
6265                ret =  e;
6266                return false;
6267             }
6268             return true;
6269             
6270         });
6271         return ret;
6272     },
6273     
6274     setActiveNext : function()
6275     {
6276         var i = this.indexOfNav(this.getActive());
6277         if (i > this.navItems.length) {
6278             return;
6279         }
6280         this.setActiveItem(this.navItems[i+1]);
6281     },
6282     setActivePrev : function()
6283     {
6284         var i = this.indexOfNav(this.getActive());
6285         if (i  < 1) {
6286             return;
6287         }
6288         this.setActiveItem(this.navItems[i-1]);
6289     },
6290     clearWasActive : function(except) {
6291         Roo.each(this.navItems, function(e) {
6292             if (e.tabId != except.tabId && e.was_active) {
6293                e.was_active = false;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299     },
6300     getWasActive : function ()
6301     {
6302         var r = false;
6303         Roo.each(this.navItems, function(e) {
6304             if (e.was_active) {
6305                r = e;
6306                return false;
6307             }
6308             return true;
6309             
6310         });
6311         return r;
6312     }
6313     
6314     
6315 });
6316
6317  
6318 Roo.apply(Roo.bootstrap.nav.Group, {
6319     
6320     groups: {},
6321      /**
6322     * register a Navigation Group
6323     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6324     */
6325     register : function(navgrp)
6326     {
6327         this.groups[navgrp.navId] = navgrp;
6328         
6329     },
6330     /**
6331     * fetch a Navigation Group based on the navigation ID
6332     * @param {string} the navgroup to add
6333     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6334     */
6335     get: function(navId) {
6336         if (typeof(this.groups[navId]) == 'undefined') {
6337             return false;
6338             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6339         }
6340         return this.groups[navId] ;
6341     }
6342     
6343     
6344     
6345 });
6346
6347  /**
6348  * @class Roo.bootstrap.nav.Item
6349  * @extends Roo.bootstrap.Component
6350  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351  * @parent Roo.bootstrap.nav.Group
6352  * @licence LGPL
6353  * Bootstrap Navbar.NavItem class
6354  * 
6355  * @cfg {String} href  link to
6356  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357  * @cfg {Boolean} button_outline show and outlined button
6358  * @cfg {String} html content of button
6359  * @cfg {String} badge text inside badge
6360  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361  * @cfg {String} glyphicon DEPRICATED - use fa
6362  * @cfg {String} icon DEPRICATED - use fa
6363  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364  * @cfg {Boolean} active Is item active
6365  * @cfg {Boolean} disabled Is item disabled
6366  * @cfg {String} linkcls  Link Class
6367  * @cfg {Boolean} preventDefault (true | false) default false
6368  * @cfg {String} tabId the tab that this item activates.
6369  * @cfg {String} tagtype (a|span) render as a href or span?
6370  * @cfg {Boolean} animateRef (true|false) link to element default false  
6371  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6372   
6373  * @constructor
6374  * Create a new Navbar Item
6375  * @param {Object} config The config object
6376  */
6377 Roo.bootstrap.nav.Item = function(config){
6378     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6379     this.addEvents({
6380         // raw events
6381         /**
6382          * @event click
6383          * The raw click event for the entire grid.
6384          * @param {Roo.EventObject} e
6385          */
6386         "click" : true,
6387          /**
6388             * @event changed
6389             * Fires when the active item active state changes
6390             * @param {Roo.bootstrap.nav.Item} this
6391             * @param {boolean} state the new state
6392              
6393          */
6394         'changed': true,
6395         /**
6396             * @event scrollto
6397             * Fires when scroll to element
6398             * @param {Roo.bootstrap.nav.Item} this
6399             * @param {Object} options
6400             * @param {Roo.EventObject} e
6401              
6402          */
6403         'scrollto': true
6404     });
6405    
6406 };
6407
6408 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6409     
6410     href: false,
6411     html: '',
6412     badge: '',
6413     icon: false,
6414     fa : false,
6415     glyphicon: false,
6416     active: false,
6417     preventDefault : false,
6418     tabId : false,
6419     tagtype : 'a',
6420     tag: 'li',
6421     disabled : false,
6422     animateRef : false,
6423     was_active : false,
6424     button_weight : '',
6425     button_outline : false,
6426     linkcls : '',
6427     navLink: false,
6428     
6429     getAutoCreate : function(){
6430          
6431         var cfg = {
6432             tag: this.tag,
6433             cls: 'nav-item'
6434         };
6435         
6436         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6437         
6438         if (this.active) {
6439             cfg.cls +=  ' active' ;
6440         }
6441         if (this.disabled) {
6442             cfg.cls += ' disabled';
6443         }
6444         
6445         // BS4 only?
6446         if (this.button_weight.length) {
6447             cfg.tag = this.href ? 'a' : 'button';
6448             cfg.html = this.html || '';
6449             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6450             if (this.href) {
6451                 cfg.href = this.href;
6452             }
6453             if (this.fa) {
6454                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6455             } else {
6456                 cfg.cls += " nav-html";
6457             }
6458             
6459             // menu .. should add dropdown-menu class - so no need for carat..
6460             
6461             if (this.badge !== '') {
6462                  
6463                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6464             }
6465             return cfg;
6466         }
6467         
6468         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6469             cfg.cn = [
6470                 {
6471                     tag: this.tagtype,
6472                     href : this.href || "#",
6473                     html: this.html || '',
6474                     cls : ''
6475                 }
6476             ];
6477             if (this.tagtype == 'a') {
6478                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6479         
6480             }
6481             if (this.icon) {
6482                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483             } else  if (this.fa) {
6484                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485             } else if(this.glyphicon) {
6486                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6487             } else {
6488                 cfg.cn[0].cls += " nav-html";
6489             }
6490             
6491             if (this.menu) {
6492                 cfg.cn[0].html += " <span class='caret'></span>";
6493              
6494             }
6495             
6496             if (this.badge !== '') {
6497                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6498             }
6499         }
6500         
6501         
6502         
6503         return cfg;
6504     },
6505     onRender : function(ct, position)
6506     {
6507        // Roo.log("Call onRender: " + this.xtype);
6508         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6509             this.tag = 'div';
6510         }
6511         
6512         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6513         this.navLink = this.el.select('.nav-link',true).first();
6514         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6515         return ret;
6516     },
6517       
6518     
6519     initEvents: function() 
6520     {
6521         if (typeof (this.menu) != 'undefined') {
6522             this.menu.parentType = this.xtype;
6523             this.menu.triggerEl = this.el;
6524             this.menu = this.addxtype(Roo.apply({}, this.menu));
6525         }
6526         
6527         this.el.on('click', this.onClick, this);
6528         
6529         //if(this.tagtype == 'span'){
6530         //    this.el.select('span',true).on('click', this.onClick, this);
6531         //}
6532        
6533         // at this point parent should be available..
6534         this.parent().register(this);
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if (e.getTarget('.dropdown-menu-item')) {
6540             // did you click on a menu itemm.... - then don't trigger onclick..
6541             return;
6542         }
6543         
6544         if(
6545                 this.preventDefault ||
6546                                 this.href === false ||
6547                 this.href === '#' 
6548         ){
6549             Roo.log("NavItem - prevent Default?");
6550             e.preventDefault();
6551         }
6552         
6553         if (this.disabled) {
6554             return;
6555         }
6556         
6557         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6558         if (tg && tg.transition) {
6559             Roo.log("waiting for the transitionend");
6560             return;
6561         }
6562         
6563         
6564         
6565         //Roo.log("fire event clicked");
6566         if(this.fireEvent('click', this, e) === false){
6567             return;
6568         };
6569         
6570         if(this.tagtype == 'span'){
6571             return;
6572         }
6573         
6574         //Roo.log(this.href);
6575         var ael = this.el.select('a',true).first();
6576         //Roo.log(ael);
6577         
6578         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6579             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6580             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6581                 return; // ignore... - it's a 'hash' to another page.
6582             }
6583             Roo.log("NavItem - prevent Default?");
6584             e.preventDefault();
6585             this.scrollToElement(e);
6586         }
6587         
6588         
6589         var p =  this.parent();
6590    
6591         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6592             if (typeof(p.setActiveItem) !== 'undefined') {
6593                 p.setActiveItem(this);
6594             }
6595         }
6596         
6597         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6598         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6599             // remove the collapsed menu expand...
6600             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6601         }
6602     },
6603     
6604     isActive: function () {
6605         return this.active
6606     },
6607     setActive : function(state, fire, is_was_active)
6608     {
6609         if (this.active && !state && this.navId) {
6610             this.was_active = true;
6611             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6612             if (nv) {
6613                 nv.clearWasActive(this);
6614             }
6615             
6616         }
6617         this.active = state;
6618         
6619         if (!state ) {
6620             this.el.removeClass('active');
6621             this.navLink ? this.navLink.removeClass('active') : false;
6622         } else if (!this.el.hasClass('active')) {
6623             
6624             this.el.addClass('active');
6625             if (Roo.bootstrap.version == 4 && this.navLink ) {
6626                 this.navLink.addClass('active');
6627             }
6628             
6629         }
6630         if (fire) {
6631             this.fireEvent('changed', this, state);
6632         }
6633         
6634         // show a panel if it's registered and related..
6635         
6636         if (!this.navId || !this.tabId || !state || is_was_active) {
6637             return;
6638         }
6639         
6640         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6641         if (!tg) {
6642             return;
6643         }
6644         var pan = tg.getPanelByName(this.tabId);
6645         if (!pan) {
6646             return;
6647         }
6648         // if we can not flip to new panel - go back to old nav highlight..
6649         if (false == tg.showPanel(pan)) {
6650             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6651             if (nv) {
6652                 var onav = nv.getWasActive();
6653                 if (onav) {
6654                     onav.setActive(true, false, true);
6655                 }
6656             }
6657             
6658         }
6659         
6660         
6661         
6662     },
6663      // this should not be here...
6664     setDisabled : function(state)
6665     {
6666         this.disabled = state;
6667         if (!state ) {
6668             this.el.removeClass('disabled');
6669         } else if (!this.el.hasClass('disabled')) {
6670             this.el.addClass('disabled');
6671         }
6672         
6673     },
6674     
6675     /**
6676      * Fetch the element to display the tooltip on.
6677      * @return {Roo.Element} defaults to this.el
6678      */
6679     tooltipEl : function()
6680     {
6681         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6682     },
6683     
6684     scrollToElement : function(e)
6685     {
6686         var c = document.body;
6687         
6688         /*
6689          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6690          */
6691         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6692             c = document.documentElement;
6693         }
6694         
6695         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6696         
6697         if(!target){
6698             return;
6699         }
6700
6701         var o = target.calcOffsetsTo(c);
6702         
6703         var options = {
6704             target : target,
6705             value : o[1]
6706         };
6707         
6708         this.fireEvent('scrollto', this, options, e);
6709         
6710         Roo.get(c).scrollTo('top', options.value, true);
6711         
6712         return;
6713     },
6714     /**
6715      * Set the HTML (text content) of the item
6716      * @param {string} html  content for the nav item
6717      */
6718     setHtml : function(html)
6719     {
6720         this.html = html;
6721         this.htmlEl.dom.innerHTML = html;
6722         
6723     } 
6724 });
6725  
6726
6727  /*
6728  * - LGPL
6729  *
6730  * sidebar item
6731  *
6732  *  li
6733  *    <span> icon </span>
6734  *    <span> text </span>
6735  *    <span>badge </span>
6736  */
6737
6738 /**
6739  * @class Roo.bootstrap.nav.SidebarItem
6740  * @extends Roo.bootstrap.nav.Item
6741  * Bootstrap Navbar.NavSidebarItem class
6742  * 
6743  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6744  * {Boolean} open is the menu open
6745  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6746  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6747  * {String} buttonSize (sm|md|lg)the extra classes for the button
6748  * {Boolean} showArrow show arrow next to the text (default true)
6749  * @constructor
6750  * Create a new Navbar Button
6751  * @param {Object} config The config object
6752  */
6753 Roo.bootstrap.nav.SidebarItem = function(config){
6754     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6755     this.addEvents({
6756         // raw events
6757         /**
6758          * @event click
6759          * The raw click event for the entire grid.
6760          * @param {Roo.EventObject} e
6761          */
6762         "click" : true,
6763          /**
6764             * @event changed
6765             * Fires when the active item active state changes
6766             * @param {Roo.bootstrap.nav.SidebarItem} this
6767             * @param {boolean} state the new state
6768              
6769          */
6770         'changed': true
6771     });
6772    
6773 };
6774
6775 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6776     
6777     badgeWeight : 'default',
6778     
6779     open: false,
6780     
6781     buttonView : false,
6782     
6783     buttonWeight : 'default',
6784     
6785     buttonSize : 'md',
6786     
6787     showArrow : true,
6788     
6789     getAutoCreate : function(){
6790         
6791         
6792         var a = {
6793                 tag: 'a',
6794                 href : this.href || '#',
6795                 cls: '',
6796                 html : '',
6797                 cn : []
6798         };
6799         
6800         if(this.buttonView){
6801             a = {
6802                 tag: 'button',
6803                 href : this.href || '#',
6804                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6805                 html : this.html,
6806                 cn : []
6807             };
6808         }
6809         
6810         var cfg = {
6811             tag: 'li',
6812             cls: '',
6813             cn: [ a ]
6814         };
6815         
6816         if (this.active) {
6817             cfg.cls += ' active';
6818         }
6819         
6820         if (this.disabled) {
6821             cfg.cls += ' disabled';
6822         }
6823         if (this.open) {
6824             cfg.cls += ' open x-open';
6825         }
6826         // left icon..
6827         if (this.glyphicon || this.icon) {
6828             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6829             a.cn.push({ tag : 'i', cls : c }) ;
6830         }
6831         
6832         if(!this.buttonView){
6833             var span = {
6834                 tag: 'span',
6835                 html : this.html || ''
6836             };
6837
6838             a.cn.push(span);
6839             
6840         }
6841         
6842         if (this.badge !== '') {
6843             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6844         }
6845         
6846         if (this.menu) {
6847             
6848             if(this.showArrow){
6849                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6850             }
6851             
6852             a.cls += ' dropdown-toggle treeview' ;
6853         }
6854         
6855         return cfg;
6856     },
6857     
6858     initEvents : function()
6859     { 
6860         if (typeof (this.menu) != 'undefined') {
6861             this.menu.parentType = this.xtype;
6862             this.menu.triggerEl = this.el;
6863             this.menu = this.addxtype(Roo.apply({}, this.menu));
6864         }
6865         
6866         this.el.on('click', this.onClick, this);
6867         
6868         if(this.badge !== ''){
6869             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6870         }
6871         
6872     },
6873     
6874     onClick : function(e)
6875     {
6876         if(this.disabled){
6877             e.preventDefault();
6878             return;
6879         }
6880         
6881         if(this.preventDefault){
6882             e.preventDefault();
6883         }
6884         
6885         this.fireEvent('click', this, e);
6886     },
6887     
6888     disable : function()
6889     {
6890         this.setDisabled(true);
6891     },
6892     
6893     enable : function()
6894     {
6895         this.setDisabled(false);
6896     },
6897     
6898     setDisabled : function(state)
6899     {
6900         if(this.disabled == state){
6901             return;
6902         }
6903         
6904         this.disabled = state;
6905         
6906         if (state) {
6907             this.el.addClass('disabled');
6908             return;
6909         }
6910         
6911         this.el.removeClass('disabled');
6912         
6913         return;
6914     },
6915     
6916     setActive : function(state)
6917     {
6918         if(this.active == state){
6919             return;
6920         }
6921         
6922         this.active = state;
6923         
6924         if (state) {
6925             this.el.addClass('active');
6926             return;
6927         }
6928         
6929         this.el.removeClass('active');
6930         
6931         return;
6932     },
6933     
6934     isActive: function () 
6935     {
6936         return this.active;
6937     },
6938     
6939     setBadge : function(str)
6940     {
6941         if(!this.badgeEl){
6942             return;
6943         }
6944         
6945         this.badgeEl.dom.innerHTML = str;
6946     }
6947     
6948    
6949      
6950  
6951 });
6952  
6953
6954  /*
6955  * - LGPL
6956  *
6957  * nav progress bar
6958  * 
6959  */
6960
6961 /**
6962  * @class Roo.bootstrap.nav.ProgressBar
6963  * @extends Roo.bootstrap.Component
6964  * @children Roo.bootstrap.nav.ProgressBarItem
6965  * Bootstrap NavProgressBar class
6966  * 
6967  * @constructor
6968  * Create a new nav progress bar - a bar indicating step along a process
6969  * @param {Object} config The config object
6970  */
6971
6972 Roo.bootstrap.nav.ProgressBar = function(config){
6973     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6974
6975     this.bullets = this.bullets || [];
6976    
6977 //    Roo.bootstrap.nav.ProgressBar.register(this);
6978      this.addEvents({
6979         /**
6980              * @event changed
6981              * Fires when the active item changes
6982              * @param {Roo.bootstrap.nav.ProgressBar} this
6983              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6984              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6985          */
6986         'changed': true
6987      });
6988     
6989 };
6990
6991 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
6992     /**
6993      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6994      * Bullets for the Nav Progress bar for the toolbar
6995      */
6996     bullets : [],
6997     barItems : [],
6998     
6999     getAutoCreate : function()
7000     {
7001         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7002         
7003         cfg = {
7004             tag : 'div',
7005             cls : 'roo-navigation-bar-group',
7006             cn : [
7007                 {
7008                     tag : 'div',
7009                     cls : 'roo-navigation-top-bar'
7010                 },
7011                 {
7012                     tag : 'div',
7013                     cls : 'roo-navigation-bullets-bar',
7014                     cn : [
7015                         {
7016                             tag : 'ul',
7017                             cls : 'roo-navigation-bar'
7018                         }
7019                     ]
7020                 },
7021                 
7022                 {
7023                     tag : 'div',
7024                     cls : 'roo-navigation-bottom-bar'
7025                 }
7026             ]
7027             
7028         };
7029         
7030         return cfg;
7031         
7032     },
7033     
7034     initEvents: function() 
7035     {
7036         
7037     },
7038     
7039     onRender : function(ct, position) 
7040     {
7041         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7042         
7043         if(this.bullets.length){
7044             Roo.each(this.bullets, function(b){
7045                this.addItem(b);
7046             }, this);
7047         }
7048         
7049         this.format();
7050         
7051     },
7052     
7053     addItem : function(cfg)
7054     {
7055         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7056         
7057         item.parentId = this.id;
7058         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7059         
7060         if(cfg.html){
7061             var top = new Roo.bootstrap.Element({
7062                 tag : 'div',
7063                 cls : 'roo-navigation-bar-text'
7064             });
7065             
7066             var bottom = new Roo.bootstrap.Element({
7067                 tag : 'div',
7068                 cls : 'roo-navigation-bar-text'
7069             });
7070             
7071             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7072             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7073             
7074             var topText = new Roo.bootstrap.Element({
7075                 tag : 'span',
7076                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7077             });
7078             
7079             var bottomText = new Roo.bootstrap.Element({
7080                 tag : 'span',
7081                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7082             });
7083             
7084             topText.onRender(top.el, null);
7085             bottomText.onRender(bottom.el, null);
7086             
7087             item.topEl = top;
7088             item.bottomEl = bottom;
7089         }
7090         
7091         this.barItems.push(item);
7092         
7093         return item;
7094     },
7095     
7096     getActive : function()
7097     {
7098         var active = false;
7099         
7100         Roo.each(this.barItems, function(v){
7101             
7102             if (!v.isActive()) {
7103                 return;
7104             }
7105             
7106             active = v;
7107             return false;
7108             
7109         });
7110         
7111         return active;
7112     },
7113     
7114     setActiveItem : function(item)
7115     {
7116         var prev = false;
7117         
7118         Roo.each(this.barItems, function(v){
7119             if (v.rid == item.rid) {
7120                 return ;
7121             }
7122             
7123             if (v.isActive()) {
7124                 v.setActive(false);
7125                 prev = v;
7126             }
7127         });
7128
7129         item.setActive(true);
7130         
7131         this.fireEvent('changed', this, item, prev);
7132     },
7133     
7134     getBarItem: function(rid)
7135     {
7136         var ret = false;
7137         
7138         Roo.each(this.barItems, function(e) {
7139             if (e.rid != rid) {
7140                 return;
7141             }
7142             
7143             ret =  e;
7144             return false;
7145         });
7146         
7147         return ret;
7148     },
7149     
7150     indexOfItem : function(item)
7151     {
7152         var index = false;
7153         
7154         Roo.each(this.barItems, function(v, i){
7155             
7156             if (v.rid != item.rid) {
7157                 return;
7158             }
7159             
7160             index = i;
7161             return false
7162         });
7163         
7164         return index;
7165     },
7166     
7167     setActiveNext : function()
7168     {
7169         var i = this.indexOfItem(this.getActive());
7170         
7171         if (i > this.barItems.length) {
7172             return;
7173         }
7174         
7175         this.setActiveItem(this.barItems[i+1]);
7176     },
7177     
7178     setActivePrev : function()
7179     {
7180         var i = this.indexOfItem(this.getActive());
7181         
7182         if (i  < 1) {
7183             return;
7184         }
7185         
7186         this.setActiveItem(this.barItems[i-1]);
7187     },
7188     
7189     format : function()
7190     {
7191         if(!this.barItems.length){
7192             return;
7193         }
7194      
7195         var width = 100 / this.barItems.length;
7196         
7197         Roo.each(this.barItems, function(i){
7198             i.el.setStyle('width', width + '%');
7199             i.topEl.el.setStyle('width', width + '%');
7200             i.bottomEl.el.setStyle('width', width + '%');
7201         }, this);
7202         
7203     }
7204     
7205 });
7206 /*
7207  * - LGPL
7208  *
7209  * Nav Progress Item
7210  * 
7211  */
7212
7213 /**
7214  * @class Roo.bootstrap.nav.ProgressBarItem
7215  * @extends Roo.bootstrap.Component
7216  * Bootstrap NavProgressBarItem class
7217  * @cfg {String} rid the reference id
7218  * @cfg {Boolean} active (true|false) Is item active default false
7219  * @cfg {Boolean} disabled (true|false) Is item active default false
7220  * @cfg {String} html
7221  * @cfg {String} position (top|bottom) text position default bottom
7222  * @cfg {String} icon show icon instead of number
7223  * 
7224  * @constructor
7225  * Create a new NavProgressBarItem
7226  * @param {Object} config The config object
7227  */
7228 Roo.bootstrap.nav.ProgressBarItem = function(config){
7229     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7230     this.addEvents({
7231         // raw events
7232         /**
7233          * @event click
7234          * The raw click event for the entire grid.
7235          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7236          * @param {Roo.EventObject} e
7237          */
7238         "click" : true
7239     });
7240    
7241 };
7242
7243 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7244     
7245     rid : '',
7246     active : false,
7247     disabled : false,
7248     html : '',
7249     position : 'bottom',
7250     icon : false,
7251     
7252     getAutoCreate : function()
7253     {
7254         var iconCls = 'roo-navigation-bar-item-icon';
7255         
7256         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7257         
7258         var cfg = {
7259             tag: 'li',
7260             cls: 'roo-navigation-bar-item',
7261             cn : [
7262                 {
7263                     tag : 'i',
7264                     cls : iconCls
7265                 }
7266             ]
7267         };
7268         
7269         if(this.active){
7270             cfg.cls += ' active';
7271         }
7272         if(this.disabled){
7273             cfg.cls += ' disabled';
7274         }
7275         
7276         return cfg;
7277     },
7278     
7279     disable : function()
7280     {
7281         this.setDisabled(true);
7282     },
7283     
7284     enable : function()
7285     {
7286         this.setDisabled(false);
7287     },
7288     
7289     initEvents: function() 
7290     {
7291         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7292         
7293         this.iconEl.on('click', this.onClick, this);
7294     },
7295     
7296     onClick : function(e)
7297     {
7298         e.preventDefault();
7299         
7300         if(this.disabled){
7301             return;
7302         }
7303         
7304         if(this.fireEvent('click', this, e) === false){
7305             return;
7306         };
7307         
7308         this.parent().setActiveItem(this);
7309     },
7310     
7311     isActive: function () 
7312     {
7313         return this.active;
7314     },
7315     
7316     setActive : function(state)
7317     {
7318         if(this.active == state){
7319             return;
7320         }
7321         
7322         this.active = state;
7323         
7324         if (state) {
7325             this.el.addClass('active');
7326             return;
7327         }
7328         
7329         this.el.removeClass('active');
7330         
7331         return;
7332     },
7333     
7334     setDisabled : function(state)
7335     {
7336         if(this.disabled == state){
7337             return;
7338         }
7339         
7340         this.disabled = state;
7341         
7342         if (state) {
7343             this.el.addClass('disabled');
7344             return;
7345         }
7346         
7347         this.el.removeClass('disabled');
7348     },
7349     
7350     tooltipEl : function()
7351     {
7352         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7353     }
7354 });
7355  
7356
7357  /*
7358  * - LGPL
7359  *
7360  *  Breadcrumb Nav
7361  * 
7362  */
7363 Roo.namespace('Roo.bootstrap.breadcrumb');
7364
7365
7366 /**
7367  * @class Roo.bootstrap.breadcrumb.Nav
7368  * @extends Roo.bootstrap.Component
7369  * Bootstrap Breadcrumb Nav Class
7370  *  
7371  * @children Roo.bootstrap.breadcrumb.Item
7372  * 
7373  * @constructor
7374  * Create a new breadcrumb.Nav
7375  * @param {Object} config The config object
7376  */
7377
7378
7379 Roo.bootstrap.breadcrumb.Nav = function(config){
7380     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7381     
7382     
7383 };
7384
7385 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7386     
7387     getAutoCreate : function()
7388     {
7389
7390         var cfg = {
7391             tag: 'nav',
7392             cn : [
7393                 {
7394                     tag : 'ol',
7395                     cls : 'breadcrumb'
7396                 }
7397             ]
7398             
7399         };
7400           
7401         return cfg;
7402     },
7403     
7404     initEvents: function()
7405     {
7406         this.olEl = this.el.select('ol',true).first();    
7407     },
7408     getChildContainer : function()
7409     {
7410         return this.olEl;  
7411     }
7412     
7413 });
7414
7415  /*
7416  * - LGPL
7417  *
7418  *  Breadcrumb Item
7419  * 
7420  */
7421
7422
7423 /**
7424  * @class Roo.bootstrap.breadcrumb.Nav
7425  * @extends Roo.bootstrap.Component
7426  * @children Roo.bootstrap.Component
7427  * @parent Roo.bootstrap.breadcrumb.Nav
7428  * Bootstrap Breadcrumb Nav Class
7429  *  
7430  * 
7431  * @cfg {String} html the content of the link.
7432  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7433  * @cfg {Boolean} active is it active
7434
7435  * 
7436  * @constructor
7437  * Create a new breadcrumb.Nav
7438  * @param {Object} config The config object
7439  */
7440
7441 Roo.bootstrap.breadcrumb.Item = function(config){
7442     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7443     this.addEvents({
7444         // img events
7445         /**
7446          * @event click
7447          * The img click event for the img.
7448          * @param {Roo.EventObject} e
7449          */
7450         "click" : true
7451     });
7452     
7453 };
7454
7455 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7456     
7457     href: false,
7458     html : '',
7459     
7460     getAutoCreate : function()
7461     {
7462
7463         var cfg = {
7464             tag: 'li',
7465             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7466         };
7467         if (this.href !== false) {
7468             cfg.cn = [{
7469                 tag : 'a',
7470                 href : this.href,
7471                 html : this.html
7472             }];
7473         } else {
7474             cfg.html = this.html;
7475         }
7476         
7477         return cfg;
7478     },
7479     
7480     initEvents: function()
7481     {
7482         if (this.href) {
7483             this.el.select('a', true).first().on('click',this.onClick, this)
7484         }
7485         
7486     },
7487     onClick : function(e)
7488     {
7489         e.preventDefault();
7490         this.fireEvent('click',this,  e);
7491     }
7492     
7493 });
7494
7495  /*
7496  * - LGPL
7497  *
7498  * row
7499  * 
7500  */
7501
7502 /**
7503  * @class Roo.bootstrap.Row
7504  * @extends Roo.bootstrap.Component
7505  * @children Roo.bootstrap.Component
7506  * Bootstrap Row class (contains columns...)
7507  * 
7508  * @constructor
7509  * Create a new Row
7510  * @param {Object} config The config object
7511  */
7512
7513 Roo.bootstrap.Row = function(config){
7514     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7515 };
7516
7517 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7518     
7519     getAutoCreate : function(){
7520        return {
7521             cls: 'row clearfix'
7522        };
7523     }
7524     
7525     
7526 });
7527
7528  
7529
7530  /*
7531  * - LGPL
7532  *
7533  * pagination
7534  * 
7535  */
7536
7537 /**
7538  * @class Roo.bootstrap.Pagination
7539  * @extends Roo.bootstrap.Component
7540  * @children Roo.bootstrap.Pagination
7541  * Bootstrap Pagination class
7542  * 
7543  * @cfg {String} size (xs|sm|md|lg|xl)
7544  * @cfg {Boolean} inverse 
7545  * 
7546  * @constructor
7547  * Create a new Pagination
7548  * @param {Object} config The config object
7549  */
7550
7551 Roo.bootstrap.Pagination = function(config){
7552     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7553 };
7554
7555 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7556     
7557     cls: false,
7558     size: false,
7559     inverse: false,
7560     
7561     getAutoCreate : function(){
7562         var cfg = {
7563             tag: 'ul',
7564                 cls: 'pagination'
7565         };
7566         if (this.inverse) {
7567             cfg.cls += ' inverse';
7568         }
7569         if (this.html) {
7570             cfg.html=this.html;
7571         }
7572         if (this.cls) {
7573             cfg.cls += " " + this.cls;
7574         }
7575         return cfg;
7576     }
7577    
7578 });
7579
7580  
7581
7582  /*
7583  * - LGPL
7584  *
7585  * Pagination item
7586  * 
7587  */
7588
7589
7590 /**
7591  * @class Roo.bootstrap.PaginationItem
7592  * @extends Roo.bootstrap.Component
7593  * Bootstrap PaginationItem class
7594  * @cfg {String} html text
7595  * @cfg {String} href the link
7596  * @cfg {Boolean} preventDefault (true | false) default true
7597  * @cfg {Boolean} active (true | false) default false
7598  * @cfg {Boolean} disabled default false
7599  * 
7600  * 
7601  * @constructor
7602  * Create a new PaginationItem
7603  * @param {Object} config The config object
7604  */
7605
7606
7607 Roo.bootstrap.PaginationItem = function(config){
7608     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7609     this.addEvents({
7610         // raw events
7611         /**
7612          * @event click
7613          * The raw click event for the entire grid.
7614          * @param {Roo.EventObject} e
7615          */
7616         "click" : true
7617     });
7618 };
7619
7620 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7621     
7622     href : false,
7623     html : false,
7624     preventDefault: true,
7625     active : false,
7626     cls : false,
7627     disabled: false,
7628     
7629     getAutoCreate : function(){
7630         var cfg= {
7631             tag: 'li',
7632             cn: [
7633                 {
7634                     tag : 'a',
7635                     href : this.href ? this.href : '#',
7636                     html : this.html ? this.html : ''
7637                 }
7638             ]
7639         };
7640         
7641         if(this.cls){
7642             cfg.cls = this.cls;
7643         }
7644         
7645         if(this.disabled){
7646             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7647         }
7648         
7649         if(this.active){
7650             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7651         }
7652         
7653         return cfg;
7654     },
7655     
7656     initEvents: function() {
7657         
7658         this.el.on('click', this.onClick, this);
7659         
7660     },
7661     onClick : function(e)
7662     {
7663         Roo.log('PaginationItem on click ');
7664         if(this.preventDefault){
7665             e.preventDefault();
7666         }
7667         
7668         if(this.disabled){
7669             return;
7670         }
7671         
7672         this.fireEvent('click', this, e);
7673     }
7674    
7675 });
7676
7677  
7678
7679  /*
7680  * - LGPL
7681  *
7682  * slider
7683  * 
7684  */
7685
7686
7687 /**
7688  * @class Roo.bootstrap.Slider
7689  * @extends Roo.bootstrap.Component
7690  * Bootstrap Slider class
7691  *    
7692  * @constructor
7693  * Create a new Slider
7694  * @param {Object} config The config object
7695  */
7696
7697 Roo.bootstrap.Slider = function(config){
7698     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7699 };
7700
7701 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7702     
7703     getAutoCreate : function(){
7704         
7705         var cfg = {
7706             tag: 'div',
7707             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7708             cn: [
7709                 {
7710                     tag: 'a',
7711                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7712                 }
7713             ]
7714         };
7715         
7716         return cfg;
7717     }
7718    
7719 });
7720
7721  /*
7722  * Based on:
7723  * Ext JS Library 1.1.1
7724  * Copyright(c) 2006-2007, Ext JS, LLC.
7725  *
7726  * Originally Released Under LGPL - original licence link has changed is not relivant.
7727  *
7728  * Fork - LGPL
7729  * <script type="text/javascript">
7730  */
7731  /**
7732  * @extends Roo.dd.DDProxy
7733  * @class Roo.grid.SplitDragZone
7734  * Support for Column Header resizing
7735  * @constructor
7736  * @param {Object} config
7737  */
7738 // private
7739 // This is a support class used internally by the Grid components
7740 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7741     this.grid = grid;
7742     this.view = grid.getView();
7743     this.proxy = this.view.resizeProxy;
7744     Roo.grid.SplitDragZone.superclass.constructor.call(
7745         this,
7746         hd, // ID
7747         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7748         {  // CONFIG
7749             dragElId : Roo.id(this.proxy.dom),
7750             resizeFrame:false
7751         }
7752     );
7753     
7754     this.setHandleElId(Roo.id(hd));
7755     if (hd2 !== false) {
7756         this.setOuterHandleElId(Roo.id(hd2));
7757     }
7758     
7759     this.scroll = false;
7760 };
7761 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7762     fly: Roo.Element.fly,
7763
7764     b4StartDrag : function(x, y){
7765         this.view.headersDisabled = true;
7766         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7767                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7768         );
7769         this.proxy.setHeight(h);
7770         
7771         // for old system colWidth really stored the actual width?
7772         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7773         // which in reality did not work.. - it worked only for fixed sizes
7774         // for resizable we need to use actual sizes.
7775         var w = this.cm.getColumnWidth(this.cellIndex);
7776         if (!this.view.mainWrap) {
7777             // bootstrap.
7778             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7779         }
7780         
7781         
7782         
7783         // this was w-this.grid.minColumnWidth;
7784         // doesnt really make sense? - w = thie curren width or the rendered one?
7785         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7786         this.resetConstraints();
7787         this.setXConstraint(minw, 1000);
7788         this.setYConstraint(0, 0);
7789         this.minX = x - minw;
7790         this.maxX = x + 1000;
7791         this.startPos = x;
7792         if (!this.view.mainWrap) { // this is Bootstrap code..
7793             this.getDragEl().style.display='block';
7794         }
7795         
7796         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7797     },
7798
7799
7800     handleMouseDown : function(e){
7801         ev = Roo.EventObject.setEvent(e);
7802         var t = this.fly(ev.getTarget());
7803         if(t.hasClass("x-grid-split")){
7804             this.cellIndex = this.view.getCellIndex(t.dom);
7805             this.split = t.dom;
7806             this.cm = this.grid.colModel;
7807             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7808                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7809             }
7810         }
7811     },
7812
7813     endDrag : function(e){
7814         this.view.headersDisabled = false;
7815         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7816         var diff = endX - this.startPos;
7817         // 
7818         var w = this.cm.getColumnWidth(this.cellIndex);
7819         if (!this.view.mainWrap) {
7820             w = 0;
7821         }
7822         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7823     },
7824
7825     autoOffset : function(){
7826         this.setDelta(0,0);
7827     }
7828 });/*
7829  * Based on:
7830  * Ext JS Library 1.1.1
7831  * Copyright(c) 2006-2007, Ext JS, LLC.
7832  *
7833  * Originally Released Under LGPL - original licence link has changed is not relivant.
7834  *
7835  * Fork - LGPL
7836  * <script type="text/javascript">
7837  */
7838
7839 /**
7840  * @class Roo.grid.AbstractSelectionModel
7841  * @extends Roo.util.Observable
7842  * @abstract
7843  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7844  * implemented by descendant classes.  This class should not be directly instantiated.
7845  * @constructor
7846  */
7847 Roo.grid.AbstractSelectionModel = function(){
7848     this.locked = false;
7849     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7850 };
7851
7852 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7853     /** @ignore Called by the grid automatically. Do not call directly. */
7854     init : function(grid){
7855         this.grid = grid;
7856         this.initEvents();
7857     },
7858
7859     /**
7860      * Locks the selections.
7861      */
7862     lock : function(){
7863         this.locked = true;
7864     },
7865
7866     /**
7867      * Unlocks the selections.
7868      */
7869     unlock : function(){
7870         this.locked = false;
7871     },
7872
7873     /**
7874      * Returns true if the selections are locked.
7875      * @return {Boolean}
7876      */
7877     isLocked : function(){
7878         return this.locked;
7879     }
7880 });/*
7881  * Based on:
7882  * Ext JS Library 1.1.1
7883  * Copyright(c) 2006-2007, Ext JS, LLC.
7884  *
7885  * Originally Released Under LGPL - original licence link has changed is not relivant.
7886  *
7887  * Fork - LGPL
7888  * <script type="text/javascript">
7889  */
7890 /**
7891  * @extends Roo.grid.AbstractSelectionModel
7892  * @class Roo.grid.RowSelectionModel
7893  * The default SelectionModel used by {@link Roo.grid.Grid}.
7894  * It supports multiple selections and keyboard selection/navigation. 
7895  * @constructor
7896  * @param {Object} config
7897  */
7898 Roo.grid.RowSelectionModel = function(config){
7899     Roo.apply(this, config);
7900     this.selections = new Roo.util.MixedCollection(false, function(o){
7901         return o.id;
7902     });
7903
7904     this.last = false;
7905     this.lastActive = false;
7906
7907     this.addEvents({
7908         /**
7909         * @event selectionchange
7910         * Fires when the selection changes
7911         * @param {SelectionModel} this
7912         */
7913        "selectionchange" : true,
7914        /**
7915         * @event afterselectionchange
7916         * Fires after the selection changes (eg. by key press or clicking)
7917         * @param {SelectionModel} this
7918         */
7919        "afterselectionchange" : true,
7920        /**
7921         * @event beforerowselect
7922         * Fires when a row is selected being selected, return false to cancel.
7923         * @param {SelectionModel} this
7924         * @param {Number} rowIndex The selected index
7925         * @param {Boolean} keepExisting False if other selections will be cleared
7926         */
7927        "beforerowselect" : true,
7928        /**
7929         * @event rowselect
7930         * Fires when a row is selected.
7931         * @param {SelectionModel} this
7932         * @param {Number} rowIndex The selected index
7933         * @param {Roo.data.Record} r The record
7934         */
7935        "rowselect" : true,
7936        /**
7937         * @event rowdeselect
7938         * Fires when a row is deselected.
7939         * @param {SelectionModel} this
7940         * @param {Number} rowIndex The selected index
7941         */
7942         "rowdeselect" : true
7943     });
7944     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7945     this.locked = false;
7946 };
7947
7948 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7949     /**
7950      * @cfg {Boolean} singleSelect
7951      * True to allow selection of only one row at a time (defaults to false)
7952      */
7953     singleSelect : false,
7954
7955     // private
7956     initEvents : function(){
7957
7958         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7959             this.grid.on("mousedown", this.handleMouseDown, this);
7960         }else{ // allow click to work like normal
7961             this.grid.on("rowclick", this.handleDragableRowClick, this);
7962         }
7963         // bootstrap does not have a view..
7964         var view = this.grid.view ? this.grid.view : this.grid;
7965         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7966             "up" : function(e){
7967                 if(!e.shiftKey){
7968                     this.selectPrevious(e.shiftKey);
7969                 }else if(this.last !== false && this.lastActive !== false){
7970                     var last = this.last;
7971                     this.selectRange(this.last,  this.lastActive-1);
7972                     view.focusRow(this.lastActive);
7973                     if(last !== false){
7974                         this.last = last;
7975                     }
7976                 }else{
7977                     this.selectFirstRow();
7978                 }
7979                 this.fireEvent("afterselectionchange", this);
7980             },
7981             "down" : function(e){
7982                 if(!e.shiftKey){
7983                     this.selectNext(e.shiftKey);
7984                 }else if(this.last !== false && this.lastActive !== false){
7985                     var last = this.last;
7986                     this.selectRange(this.last,  this.lastActive+1);
7987                     view.focusRow(this.lastActive);
7988                     if(last !== false){
7989                         this.last = last;
7990                     }
7991                 }else{
7992                     this.selectFirstRow();
7993                 }
7994                 this.fireEvent("afterselectionchange", this);
7995             },
7996             scope: this
7997         });
7998
7999          
8000         view.on("refresh", this.onRefresh, this);
8001         view.on("rowupdated", this.onRowUpdated, this);
8002         view.on("rowremoved", this.onRemove, this);
8003     },
8004
8005     // private
8006     onRefresh : function(){
8007         var ds = this.grid.ds, i, v = this.grid.view;
8008         var s = this.selections;
8009         s.each(function(r){
8010             if((i = ds.indexOfId(r.id)) != -1){
8011                 v.onRowSelect(i);
8012                 s.add(ds.getAt(i)); // updating the selection relate data
8013             }else{
8014                 s.remove(r);
8015             }
8016         });
8017     },
8018
8019     // private
8020     onRemove : function(v, index, r){
8021         this.selections.remove(r);
8022     },
8023
8024     // private
8025     onRowUpdated : function(v, index, r){
8026         if(this.isSelected(r)){
8027             v.onRowSelect(index);
8028         }
8029     },
8030
8031     /**
8032      * Select records.
8033      * @param {Array} records The records to select
8034      * @param {Boolean} keepExisting (optional) True to keep existing selections
8035      */
8036     selectRecords : function(records, keepExisting){
8037         if(!keepExisting){
8038             this.clearSelections();
8039         }
8040         var ds = this.grid.ds;
8041         for(var i = 0, len = records.length; i < len; i++){
8042             this.selectRow(ds.indexOf(records[i]), true);
8043         }
8044     },
8045
8046     /**
8047      * Gets the number of selected rows.
8048      * @return {Number}
8049      */
8050     getCount : function(){
8051         return this.selections.length;
8052     },
8053
8054     /**
8055      * Selects the first row in the grid.
8056      */
8057     selectFirstRow : function(){
8058         this.selectRow(0);
8059     },
8060
8061     /**
8062      * Select the last row.
8063      * @param {Boolean} keepExisting (optional) True to keep existing selections
8064      */
8065     selectLastRow : function(keepExisting){
8066         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8067     },
8068
8069     /**
8070      * Selects the row immediately following the last selected row.
8071      * @param {Boolean} keepExisting (optional) True to keep existing selections
8072      */
8073     selectNext : function(keepExisting){
8074         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8075             this.selectRow(this.last+1, keepExisting);
8076             var view = this.grid.view ? this.grid.view : this.grid;
8077             view.focusRow(this.last);
8078         }
8079     },
8080
8081     /**
8082      * Selects the row that precedes the last selected row.
8083      * @param {Boolean} keepExisting (optional) True to keep existing selections
8084      */
8085     selectPrevious : function(keepExisting){
8086         if(this.last){
8087             this.selectRow(this.last-1, keepExisting);
8088             var view = this.grid.view ? this.grid.view : this.grid;
8089             view.focusRow(this.last);
8090         }
8091     },
8092
8093     /**
8094      * Returns the selected records
8095      * @return {Array} Array of selected records
8096      */
8097     getSelections : function(){
8098         return [].concat(this.selections.items);
8099     },
8100
8101     /**
8102      * Returns the first selected record.
8103      * @return {Record}
8104      */
8105     getSelected : function(){
8106         return this.selections.itemAt(0);
8107     },
8108
8109
8110     /**
8111      * Clears all selections.
8112      */
8113     clearSelections : function(fast){
8114         if(this.locked) {
8115             return;
8116         }
8117         if(fast !== true){
8118             var ds = this.grid.ds;
8119             var s = this.selections;
8120             s.each(function(r){
8121                 this.deselectRow(ds.indexOfId(r.id));
8122             }, this);
8123             s.clear();
8124         }else{
8125             this.selections.clear();
8126         }
8127         this.last = false;
8128     },
8129
8130
8131     /**
8132      * Selects all rows.
8133      */
8134     selectAll : function(){
8135         if(this.locked) {
8136             return;
8137         }
8138         this.selections.clear();
8139         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8140             this.selectRow(i, true);
8141         }
8142     },
8143
8144     /**
8145      * Returns True if there is a selection.
8146      * @return {Boolean}
8147      */
8148     hasSelection : function(){
8149         return this.selections.length > 0;
8150     },
8151
8152     /**
8153      * Returns True if the specified row is selected.
8154      * @param {Number/Record} record The record or index of the record to check
8155      * @return {Boolean}
8156      */
8157     isSelected : function(index){
8158         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8159         return (r && this.selections.key(r.id) ? true : false);
8160     },
8161
8162     /**
8163      * Returns True if the specified record id is selected.
8164      * @param {String} id The id of record to check
8165      * @return {Boolean}
8166      */
8167     isIdSelected : function(id){
8168         return (this.selections.key(id) ? true : false);
8169     },
8170
8171     // private
8172     handleMouseDown : function(e, t)
8173     {
8174         var view = this.grid.view ? this.grid.view : this.grid;
8175         var rowIndex;
8176         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8177             return;
8178         };
8179         if(e.shiftKey && this.last !== false){
8180             var last = this.last;
8181             this.selectRange(last, rowIndex, e.ctrlKey);
8182             this.last = last; // reset the last
8183             view.focusRow(rowIndex);
8184         }else{
8185             var isSelected = this.isSelected(rowIndex);
8186             if(e.button !== 0 && isSelected){
8187                 view.focusRow(rowIndex);
8188             }else if(e.ctrlKey && isSelected){
8189                 this.deselectRow(rowIndex);
8190             }else if(!isSelected){
8191                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8192                 view.focusRow(rowIndex);
8193             }
8194         }
8195         this.fireEvent("afterselectionchange", this);
8196     },
8197     // private
8198     handleDragableRowClick :  function(grid, rowIndex, e) 
8199     {
8200         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8201             this.selectRow(rowIndex, false);
8202             var view = this.grid.view ? this.grid.view : this.grid;
8203             view.focusRow(rowIndex);
8204              this.fireEvent("afterselectionchange", this);
8205         }
8206     },
8207     
8208     /**
8209      * Selects multiple rows.
8210      * @param {Array} rows Array of the indexes of the row to select
8211      * @param {Boolean} keepExisting (optional) True to keep existing selections
8212      */
8213     selectRows : function(rows, keepExisting){
8214         if(!keepExisting){
8215             this.clearSelections();
8216         }
8217         for(var i = 0, len = rows.length; i < len; i++){
8218             this.selectRow(rows[i], true);
8219         }
8220     },
8221
8222     /**
8223      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8224      * @param {Number} startRow The index of the first row in the range
8225      * @param {Number} endRow The index of the last row in the range
8226      * @param {Boolean} keepExisting (optional) True to retain existing selections
8227      */
8228     selectRange : function(startRow, endRow, keepExisting){
8229         if(this.locked) {
8230             return;
8231         }
8232         if(!keepExisting){
8233             this.clearSelections();
8234         }
8235         if(startRow <= endRow){
8236             for(var i = startRow; i <= endRow; i++){
8237                 this.selectRow(i, true);
8238             }
8239         }else{
8240             for(var i = startRow; i >= endRow; i--){
8241                 this.selectRow(i, true);
8242             }
8243         }
8244     },
8245
8246     /**
8247      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8248      * @param {Number} startRow The index of the first row in the range
8249      * @param {Number} endRow The index of the last row in the range
8250      */
8251     deselectRange : function(startRow, endRow, preventViewNotify){
8252         if(this.locked) {
8253             return;
8254         }
8255         for(var i = startRow; i <= endRow; i++){
8256             this.deselectRow(i, preventViewNotify);
8257         }
8258     },
8259
8260     /**
8261      * Selects a row.
8262      * @param {Number} row The index of the row to select
8263      * @param {Boolean} keepExisting (optional) True to keep existing selections
8264      */
8265     selectRow : function(index, keepExisting, preventViewNotify){
8266         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8267             return;
8268         }
8269         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8270             if(!keepExisting || this.singleSelect){
8271                 this.clearSelections();
8272             }
8273             var r = this.grid.ds.getAt(index);
8274             this.selections.add(r);
8275             this.last = this.lastActive = index;
8276             if(!preventViewNotify){
8277                 var view = this.grid.view ? this.grid.view : this.grid;
8278                 view.onRowSelect(index);
8279             }
8280             this.fireEvent("rowselect", this, index, r);
8281             this.fireEvent("selectionchange", this);
8282         }
8283     },
8284
8285     /**
8286      * Deselects a row.
8287      * @param {Number} row The index of the row to deselect
8288      */
8289     deselectRow : function(index, preventViewNotify){
8290         if(this.locked) {
8291             return;
8292         }
8293         if(this.last == index){
8294             this.last = false;
8295         }
8296         if(this.lastActive == index){
8297             this.lastActive = false;
8298         }
8299         var r = this.grid.ds.getAt(index);
8300         this.selections.remove(r);
8301         if(!preventViewNotify){
8302             var view = this.grid.view ? this.grid.view : this.grid;
8303             view.onRowDeselect(index);
8304         }
8305         this.fireEvent("rowdeselect", this, index);
8306         this.fireEvent("selectionchange", this);
8307     },
8308
8309     // private
8310     restoreLast : function(){
8311         if(this._last){
8312             this.last = this._last;
8313         }
8314     },
8315
8316     // private
8317     acceptsNav : function(row, col, cm){
8318         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8319     },
8320
8321     // private
8322     onEditorKey : function(field, e){
8323         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8324         if(k == e.TAB){
8325             e.stopEvent();
8326             ed.completeEdit();
8327             if(e.shiftKey){
8328                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8329             }else{
8330                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8331             }
8332         }else if(k == e.ENTER && !e.ctrlKey){
8333             e.stopEvent();
8334             ed.completeEdit();
8335             if(e.shiftKey){
8336                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8337             }else{
8338                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8339             }
8340         }else if(k == e.ESC){
8341             ed.cancelEdit();
8342         }
8343         if(newCell){
8344             g.startEditing(newCell[0], newCell[1]);
8345         }
8346     }
8347 });/*
8348  * Based on:
8349  * Ext JS Library 1.1.1
8350  * Copyright(c) 2006-2007, Ext JS, LLC.
8351  *
8352  * Originally Released Under LGPL - original licence link has changed is not relivant.
8353  *
8354  * Fork - LGPL
8355  * <script type="text/javascript">
8356  */
8357  
8358
8359 /**
8360  * @class Roo.grid.ColumnModel
8361  * @extends Roo.util.Observable
8362  * This is the default implementation of a ColumnModel used by the Grid. It defines
8363  * the columns in the grid.
8364  * <br>Usage:<br>
8365  <pre><code>
8366  var colModel = new Roo.grid.ColumnModel([
8367         {header: "Ticker", width: 60, sortable: true, locked: true},
8368         {header: "Company Name", width: 150, sortable: true},
8369         {header: "Market Cap.", width: 100, sortable: true},
8370         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8371         {header: "Employees", width: 100, sortable: true, resizable: false}
8372  ]);
8373  </code></pre>
8374  * <p>
8375  
8376  * The config options listed for this class are options which may appear in each
8377  * individual column definition.
8378  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8379  * @constructor
8380  * @param {Object} config An Array of column config objects. See this class's
8381  * config objects for details.
8382 */
8383 Roo.grid.ColumnModel = function(config){
8384         /**
8385      * The config passed into the constructor
8386      */
8387     this.config = []; //config;
8388     this.lookup = {};
8389
8390     // if no id, create one
8391     // if the column does not have a dataIndex mapping,
8392     // map it to the order it is in the config
8393     for(var i = 0, len = config.length; i < len; i++){
8394         this.addColumn(config[i]);
8395         
8396     }
8397
8398     /**
8399      * The width of columns which have no width specified (defaults to 100)
8400      * @type Number
8401      */
8402     this.defaultWidth = 100;
8403
8404     /**
8405      * Default sortable of columns which have no sortable specified (defaults to false)
8406      * @type Boolean
8407      */
8408     this.defaultSortable = false;
8409
8410     this.addEvents({
8411         /**
8412              * @event widthchange
8413              * Fires when the width of a column changes.
8414              * @param {ColumnModel} this
8415              * @param {Number} columnIndex The column index
8416              * @param {Number} newWidth The new width
8417              */
8418             "widthchange": true,
8419         /**
8420              * @event headerchange
8421              * Fires when the text of a header changes.
8422              * @param {ColumnModel} this
8423              * @param {Number} columnIndex The column index
8424              * @param {Number} newText The new header text
8425              */
8426             "headerchange": true,
8427         /**
8428              * @event hiddenchange
8429              * Fires when a column is hidden or "unhidden".
8430              * @param {ColumnModel} this
8431              * @param {Number} columnIndex The column index
8432              * @param {Boolean} hidden true if hidden, false otherwise
8433              */
8434             "hiddenchange": true,
8435             /**
8436          * @event columnmoved
8437          * Fires when a column is moved.
8438          * @param {ColumnModel} this
8439          * @param {Number} oldIndex
8440          * @param {Number} newIndex
8441          */
8442         "columnmoved" : true,
8443         /**
8444          * @event columlockchange
8445          * Fires when a column's locked state is changed
8446          * @param {ColumnModel} this
8447          * @param {Number} colIndex
8448          * @param {Boolean} locked true if locked
8449          */
8450         "columnlockchange" : true
8451     });
8452     Roo.grid.ColumnModel.superclass.constructor.call(this);
8453 };
8454 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8455     /**
8456      * @cfg {String} header The header text to display in the Grid view.
8457      */
8458         /**
8459      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8460      */
8461         /**
8462      * @cfg {String} smHeader Header at Bootsrap Small width
8463      */
8464         /**
8465      * @cfg {String} mdHeader Header at Bootsrap Medium width
8466      */
8467         /**
8468      * @cfg {String} lgHeader Header at Bootsrap Large width
8469      */
8470         /**
8471      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8472      */
8473     /**
8474      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8475      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8476      * specified, the column's index is used as an index into the Record's data Array.
8477      */
8478     /**
8479      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8480      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8481      */
8482     /**
8483      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8484      * Defaults to the value of the {@link #defaultSortable} property.
8485      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8486      */
8487     /**
8488      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8489      */
8490     /**
8491      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8492      */
8493     /**
8494      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8495      */
8496     /**
8497      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8498      */
8499     /**
8500      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8501      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8502      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8503      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8504      */
8505        /**
8506      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8507      */
8508     /**
8509      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8510      */
8511     /**
8512      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8513      */
8514     /**
8515      * @cfg {String} cursor (Optional)
8516      */
8517     /**
8518      * @cfg {String} tooltip (Optional)
8519      */
8520     /**
8521      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8522      */
8523     /**
8524      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8525      */
8526     /**
8527      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8528      */
8529     /**
8530      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8531      */
8532         /**
8533      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8534      */
8535     /**
8536      * Returns the id of the column at the specified index.
8537      * @param {Number} index The column index
8538      * @return {String} the id
8539      */
8540     getColumnId : function(index){
8541         return this.config[index].id;
8542     },
8543
8544     /**
8545      * Returns the column for a specified id.
8546      * @param {String} id The column id
8547      * @return {Object} the column
8548      */
8549     getColumnById : function(id){
8550         return this.lookup[id];
8551     },
8552
8553     
8554     /**
8555      * Returns the column Object for a specified dataIndex.
8556      * @param {String} dataIndex The column dataIndex
8557      * @return {Object|Boolean} the column or false if not found
8558      */
8559     getColumnByDataIndex: function(dataIndex){
8560         var index = this.findColumnIndex(dataIndex);
8561         return index > -1 ? this.config[index] : false;
8562     },
8563     
8564     /**
8565      * Returns the index for a specified column id.
8566      * @param {String} id The column id
8567      * @return {Number} the index, or -1 if not found
8568      */
8569     getIndexById : function(id){
8570         for(var i = 0, len = this.config.length; i < len; i++){
8571             if(this.config[i].id == id){
8572                 return i;
8573             }
8574         }
8575         return -1;
8576     },
8577     
8578     /**
8579      * Returns the index for a specified column dataIndex.
8580      * @param {String} dataIndex The column dataIndex
8581      * @return {Number} the index, or -1 if not found
8582      */
8583     
8584     findColumnIndex : function(dataIndex){
8585         for(var i = 0, len = this.config.length; i < len; i++){
8586             if(this.config[i].dataIndex == dataIndex){
8587                 return i;
8588             }
8589         }
8590         return -1;
8591     },
8592     
8593     
8594     moveColumn : function(oldIndex, newIndex){
8595         var c = this.config[oldIndex];
8596         this.config.splice(oldIndex, 1);
8597         this.config.splice(newIndex, 0, c);
8598         this.dataMap = null;
8599         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8600     },
8601
8602     isLocked : function(colIndex){
8603         return this.config[colIndex].locked === true;
8604     },
8605
8606     setLocked : function(colIndex, value, suppressEvent){
8607         if(this.isLocked(colIndex) == value){
8608             return;
8609         }
8610         this.config[colIndex].locked = value;
8611         if(!suppressEvent){
8612             this.fireEvent("columnlockchange", this, colIndex, value);
8613         }
8614     },
8615
8616     getTotalLockedWidth : function(){
8617         var totalWidth = 0;
8618         for(var i = 0; i < this.config.length; i++){
8619             if(this.isLocked(i) && !this.isHidden(i)){
8620                 this.totalWidth += this.getColumnWidth(i);
8621             }
8622         }
8623         return totalWidth;
8624     },
8625
8626     getLockedCount : function(){
8627         for(var i = 0, len = this.config.length; i < len; i++){
8628             if(!this.isLocked(i)){
8629                 return i;
8630             }
8631         }
8632         
8633         return this.config.length;
8634     },
8635
8636     /**
8637      * Returns the number of columns.
8638      * @return {Number}
8639      */
8640     getColumnCount : function(visibleOnly){
8641         if(visibleOnly === true){
8642             var c = 0;
8643             for(var i = 0, len = this.config.length; i < len; i++){
8644                 if(!this.isHidden(i)){
8645                     c++;
8646                 }
8647             }
8648             return c;
8649         }
8650         return this.config.length;
8651     },
8652
8653     /**
8654      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8655      * @param {Function} fn
8656      * @param {Object} scope (optional)
8657      * @return {Array} result
8658      */
8659     getColumnsBy : function(fn, scope){
8660         var r = [];
8661         for(var i = 0, len = this.config.length; i < len; i++){
8662             var c = this.config[i];
8663             if(fn.call(scope||this, c, i) === true){
8664                 r[r.length] = c;
8665             }
8666         }
8667         return r;
8668     },
8669
8670     /**
8671      * Returns true if the specified column is sortable.
8672      * @param {Number} col The column index
8673      * @return {Boolean}
8674      */
8675     isSortable : function(col){
8676         if(typeof this.config[col].sortable == "undefined"){
8677             return this.defaultSortable;
8678         }
8679         return this.config[col].sortable;
8680     },
8681
8682     /**
8683      * Returns the rendering (formatting) function defined for the column.
8684      * @param {Number} col The column index.
8685      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8686      */
8687     getRenderer : function(col){
8688         if(!this.config[col].renderer){
8689             return Roo.grid.ColumnModel.defaultRenderer;
8690         }
8691         return this.config[col].renderer;
8692     },
8693
8694     /**
8695      * Sets the rendering (formatting) function for a column.
8696      * @param {Number} col The column index
8697      * @param {Function} fn The function to use to process the cell's raw data
8698      * to return HTML markup for the grid view. The render function is called with
8699      * the following parameters:<ul>
8700      * <li>Data value.</li>
8701      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8702      * <li>css A CSS style string to apply to the table cell.</li>
8703      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8704      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8705      * <li>Row index</li>
8706      * <li>Column index</li>
8707      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8708      */
8709     setRenderer : function(col, fn){
8710         this.config[col].renderer = fn;
8711     },
8712
8713     /**
8714      * Returns the width for the specified column.
8715      * @param {Number} col The column index
8716      * @param (optional) {String} gridSize bootstrap width size.
8717      * @return {Number}
8718      */
8719     getColumnWidth : function(col, gridSize)
8720         {
8721                 var cfg = this.config[col];
8722                 
8723                 if (typeof(gridSize) == 'undefined') {
8724                         return cfg.width * 1 || this.defaultWidth;
8725                 }
8726                 if (gridSize === false) { // if we set it..
8727                         return cfg.width || false;
8728                 }
8729                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8730                 
8731                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8732                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8733                                 continue;
8734                         }
8735                         return cfg[ sizes[i] ];
8736                 }
8737                 return 1;
8738                 
8739     },
8740
8741     /**
8742      * Sets the width for a column.
8743      * @param {Number} col The column index
8744      * @param {Number} width The new width
8745      */
8746     setColumnWidth : function(col, width, suppressEvent){
8747         this.config[col].width = width;
8748         this.totalWidth = null;
8749         if(!suppressEvent){
8750              this.fireEvent("widthchange", this, col, width);
8751         }
8752     },
8753
8754     /**
8755      * Returns the total width of all columns.
8756      * @param {Boolean} includeHidden True to include hidden column widths
8757      * @return {Number}
8758      */
8759     getTotalWidth : function(includeHidden){
8760         if(!this.totalWidth){
8761             this.totalWidth = 0;
8762             for(var i = 0, len = this.config.length; i < len; i++){
8763                 if(includeHidden || !this.isHidden(i)){
8764                     this.totalWidth += this.getColumnWidth(i);
8765                 }
8766             }
8767         }
8768         return this.totalWidth;
8769     },
8770
8771     /**
8772      * Returns the header for the specified column.
8773      * @param {Number} col The column index
8774      * @return {String}
8775      */
8776     getColumnHeader : function(col){
8777         return this.config[col].header;
8778     },
8779
8780     /**
8781      * Sets the header for a column.
8782      * @param {Number} col The column index
8783      * @param {String} header The new header
8784      */
8785     setColumnHeader : function(col, header){
8786         this.config[col].header = header;
8787         this.fireEvent("headerchange", this, col, header);
8788     },
8789
8790     /**
8791      * Returns the tooltip for the specified column.
8792      * @param {Number} col The column index
8793      * @return {String}
8794      */
8795     getColumnTooltip : function(col){
8796             return this.config[col].tooltip;
8797     },
8798     /**
8799      * Sets the tooltip for a column.
8800      * @param {Number} col The column index
8801      * @param {String} tooltip The new tooltip
8802      */
8803     setColumnTooltip : function(col, tooltip){
8804             this.config[col].tooltip = tooltip;
8805     },
8806
8807     /**
8808      * Returns the dataIndex for the specified column.
8809      * @param {Number} col The column index
8810      * @return {Number}
8811      */
8812     getDataIndex : function(col){
8813         return this.config[col].dataIndex;
8814     },
8815
8816     /**
8817      * Sets the dataIndex for a column.
8818      * @param {Number} col The column index
8819      * @param {Number} dataIndex The new dataIndex
8820      */
8821     setDataIndex : function(col, dataIndex){
8822         this.config[col].dataIndex = dataIndex;
8823     },
8824
8825     
8826     
8827     /**
8828      * Returns true if the cell is editable.
8829      * @param {Number} colIndex The column index
8830      * @param {Number} rowIndex The row index - this is nto actually used..?
8831      * @return {Boolean}
8832      */
8833     isCellEditable : function(colIndex, rowIndex){
8834         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8835     },
8836
8837     /**
8838      * Returns the editor defined for the cell/column.
8839      * return false or null to disable editing.
8840      * @param {Number} colIndex The column index
8841      * @param {Number} rowIndex The row index
8842      * @return {Object}
8843      */
8844     getCellEditor : function(colIndex, rowIndex){
8845         return this.config[colIndex].editor;
8846     },
8847
8848     /**
8849      * Sets if a column is editable.
8850      * @param {Number} col The column index
8851      * @param {Boolean} editable True if the column is editable
8852      */
8853     setEditable : function(col, editable){
8854         this.config[col].editable = editable;
8855     },
8856
8857
8858     /**
8859      * Returns true if the column is hidden.
8860      * @param {Number} colIndex The column index
8861      * @return {Boolean}
8862      */
8863     isHidden : function(colIndex){
8864         return this.config[colIndex].hidden;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column width cannot be changed
8870      */
8871     isFixed : function(colIndex){
8872         return this.config[colIndex].fixed;
8873     },
8874
8875     /**
8876      * Returns true if the column can be resized
8877      * @return {Boolean}
8878      */
8879     isResizable : function(colIndex){
8880         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8881     },
8882     /**
8883      * Sets if a column is hidden.
8884      * @param {Number} colIndex The column index
8885      * @param {Boolean} hidden True if the column is hidden
8886      */
8887     setHidden : function(colIndex, hidden){
8888         this.config[colIndex].hidden = hidden;
8889         this.totalWidth = null;
8890         this.fireEvent("hiddenchange", this, colIndex, hidden);
8891     },
8892
8893     /**
8894      * Sets the editor for a column.
8895      * @param {Number} col The column index
8896      * @param {Object} editor The editor object
8897      */
8898     setEditor : function(col, editor){
8899         this.config[col].editor = editor;
8900     },
8901     /**
8902      * Add a column (experimental...) - defaults to adding to the end..
8903      * @param {Object} config 
8904     */
8905     addColumn : function(c)
8906     {
8907     
8908         var i = this.config.length;
8909         this.config[i] = c;
8910         
8911         if(typeof c.dataIndex == "undefined"){
8912             c.dataIndex = i;
8913         }
8914         if(typeof c.renderer == "string"){
8915             c.renderer = Roo.util.Format[c.renderer];
8916         }
8917         if(typeof c.id == "undefined"){
8918             c.id = Roo.id();
8919         }
8920         if(c.editor && c.editor.xtype){
8921             c.editor  = Roo.factory(c.editor, Roo.grid);
8922         }
8923         if(c.editor && c.editor.isFormField){
8924             c.editor = new Roo.grid.GridEditor(c.editor);
8925         }
8926         this.lookup[c.id] = c;
8927     }
8928     
8929 });
8930
8931 Roo.grid.ColumnModel.defaultRenderer = function(value)
8932 {
8933     if(typeof value == "object") {
8934         return value;
8935     }
8936         if(typeof value == "string" && value.length < 1){
8937             return "&#160;";
8938         }
8939     
8940         return String.format("{0}", value);
8941 };
8942
8943 // Alias for backwards compatibility
8944 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8945 /*
8946  * Based on:
8947  * Ext JS Library 1.1.1
8948  * Copyright(c) 2006-2007, Ext JS, LLC.
8949  *
8950  * Originally Released Under LGPL - original licence link has changed is not relivant.
8951  *
8952  * Fork - LGPL
8953  * <script type="text/javascript">
8954  */
8955  
8956 /**
8957  * @class Roo.LoadMask
8958  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8959  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8960  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8961  * element's UpdateManager load indicator and will be destroyed after the initial load.
8962  * @constructor
8963  * Create a new LoadMask
8964  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8965  * @param {Object} config The config object
8966  */
8967 Roo.LoadMask = function(el, config){
8968     this.el = Roo.get(el);
8969     Roo.apply(this, config);
8970     if(this.store){
8971         this.store.on('beforeload', this.onBeforeLoad, this);
8972         this.store.on('load', this.onLoad, this);
8973         this.store.on('loadexception', this.onLoadException, this);
8974         this.removeMask = false;
8975     }else{
8976         var um = this.el.getUpdateManager();
8977         um.showLoadIndicator = false; // disable the default indicator
8978         um.on('beforeupdate', this.onBeforeLoad, this);
8979         um.on('update', this.onLoad, this);
8980         um.on('failure', this.onLoad, this);
8981         this.removeMask = true;
8982     }
8983 };
8984
8985 Roo.LoadMask.prototype = {
8986     /**
8987      * @cfg {Boolean} removeMask
8988      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8989      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8990      */
8991     removeMask : false,
8992     /**
8993      * @cfg {String} msg
8994      * The text to display in a centered loading message box (defaults to 'Loading...')
8995      */
8996     msg : 'Loading...',
8997     /**
8998      * @cfg {String} msgCls
8999      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9000      */
9001     msgCls : 'x-mask-loading',
9002
9003     /**
9004      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9005      * @type Boolean
9006      */
9007     disabled: false,
9008
9009     /**
9010      * Disables the mask to prevent it from being displayed
9011      */
9012     disable : function(){
9013        this.disabled = true;
9014     },
9015
9016     /**
9017      * Enables the mask so that it can be displayed
9018      */
9019     enable : function(){
9020         this.disabled = false;
9021     },
9022     
9023     onLoadException : function()
9024     {
9025         Roo.log(arguments);
9026         
9027         if (typeof(arguments[3]) != 'undefined') {
9028             Roo.MessageBox.alert("Error loading",arguments[3]);
9029         } 
9030         /*
9031         try {
9032             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9033                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9034             }   
9035         } catch(e) {
9036             
9037         }
9038         */
9039     
9040         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9041     },
9042     // private
9043     onLoad : function()
9044     {
9045         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9046     },
9047
9048     // private
9049     onBeforeLoad : function(){
9050         if(!this.disabled){
9051             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9052         }
9053     },
9054
9055     // private
9056     destroy : function(){
9057         if(this.store){
9058             this.store.un('beforeload', this.onBeforeLoad, this);
9059             this.store.un('load', this.onLoad, this);
9060             this.store.un('loadexception', this.onLoadException, this);
9061         }else{
9062             var um = this.el.getUpdateManager();
9063             um.un('beforeupdate', this.onBeforeLoad, this);
9064             um.un('update', this.onLoad, this);
9065             um.un('failure', this.onLoad, this);
9066         }
9067     }
9068 };/**
9069  * @class Roo.bootstrap.Table
9070  * @licence LGBL
9071  * @extends Roo.bootstrap.Component
9072  * @children Roo.bootstrap.TableBody
9073  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9074  * Similar to Roo.grid.Grid
9075  * <pre><code>
9076  var table = Roo.factory({
9077     xtype : 'Table',
9078     xns : Roo.bootstrap,
9079     autoSizeColumns: true,
9080     
9081     
9082     store : {
9083         xtype : 'Store',
9084         xns : Roo.data,
9085         remoteSort : true,
9086         sortInfo : { direction : 'ASC', field: 'name' },
9087         proxy : {
9088            xtype : 'HttpProxy',
9089            xns : Roo.data,
9090            method : 'GET',
9091            url : 'https://example.com/some.data.url.json'
9092         },
9093         reader : {
9094            xtype : 'JsonReader',
9095            xns : Roo.data,
9096            fields : [ 'id', 'name', whatever' ],
9097            id : 'id',
9098            root : 'data'
9099         }
9100     },
9101     cm : [
9102         {
9103             xtype : 'ColumnModel',
9104             xns : Roo.grid,
9105             align : 'center',
9106             cursor : 'pointer',
9107             dataIndex : 'is_in_group',
9108             header : "Name",
9109             sortable : true,
9110             renderer : function(v, x , r) {  
9111             
9112                 return String.format("{0}", v)
9113             }
9114             width : 3
9115         } // more columns..
9116     ],
9117     selModel : {
9118         xtype : 'RowSelectionModel',
9119         xns : Roo.bootstrap.Table
9120         // you can add listeners to catch selection change here....
9121     }
9122      
9123
9124  });
9125  // set any options
9126  grid.render(Roo.get("some-div"));
9127 </code></pre>
9128
9129 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9130
9131
9132
9133  *
9134  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9135  * @cfg {Roo.data.Store} store The data store to use
9136  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9137  * 
9138  * @cfg {String} cls table class
9139  *
9140  *
9141  * @cfg {string} empty_results  Text to display for no results 
9142  * @cfg {boolean} striped Should the rows be alternative striped
9143  * @cfg {boolean} bordered Add borders to the table
9144  * @cfg {boolean} hover Add hover highlighting
9145  * @cfg {boolean} condensed Format condensed
9146  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9147  *                also adds table-responsive (see bootstrap docs for details)
9148  * @cfg {Boolean} loadMask (true|false) default false
9149  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9150  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9151  * @cfg {Boolean} rowSelection (true|false) default false
9152  * @cfg {Boolean} cellSelection (true|false) default false
9153  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9154  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9155  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9156  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9157  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9158  *
9159  * 
9160  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9161  * 
9162  * @constructor
9163  * Create a new Table
9164  * @param {Object} config The config object
9165  */
9166
9167 Roo.bootstrap.Table = function(config)
9168 {
9169     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9170      
9171     // BC...
9172     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9173     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9174     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9175     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9176     
9177     this.view = this; // compat with grid.
9178     
9179     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9180     if (this.sm) {
9181         this.sm.grid = this;
9182         this.selModel = Roo.factory(this.sm, Roo.grid);
9183         this.sm = this.selModel;
9184         this.sm.xmodule = this.xmodule || false;
9185     }
9186     
9187     if (this.cm && typeof(this.cm.config) == 'undefined') {
9188         this.colModel = new Roo.grid.ColumnModel(this.cm);
9189         this.cm = this.colModel;
9190         this.cm.xmodule = this.xmodule || false;
9191     }
9192     if (this.store) {
9193         this.store= Roo.factory(this.store, Roo.data);
9194         this.ds = this.store;
9195         this.ds.xmodule = this.xmodule || false;
9196          
9197     }
9198     if (this.footer && this.store) {
9199         this.footer.dataSource = this.ds;
9200         this.footer = Roo.factory(this.footer);
9201     }
9202     
9203     /** @private */
9204     this.addEvents({
9205         /**
9206          * @event cellclick
9207          * Fires when a cell is clicked
9208          * @param {Roo.bootstrap.Table} this
9209          * @param {Roo.Element} el
9210          * @param {Number} rowIndex
9211          * @param {Number} columnIndex
9212          * @param {Roo.EventObject} e
9213          */
9214         "cellclick" : true,
9215         /**
9216          * @event celldblclick
9217          * Fires when a cell is double clicked
9218          * @param {Roo.bootstrap.Table} this
9219          * @param {Roo.Element} el
9220          * @param {Number} rowIndex
9221          * @param {Number} columnIndex
9222          * @param {Roo.EventObject} e
9223          */
9224         "celldblclick" : true,
9225         /**
9226          * @event rowclick
9227          * Fires when a row is clicked
9228          * @param {Roo.bootstrap.Table} this
9229          * @param {Roo.Element} el
9230          * @param {Number} rowIndex
9231          * @param {Roo.EventObject} e
9232          */
9233         "rowclick" : true,
9234         /**
9235          * @event rowdblclick
9236          * Fires when a row is double clicked
9237          * @param {Roo.bootstrap.Table} this
9238          * @param {Roo.Element} el
9239          * @param {Number} rowIndex
9240          * @param {Roo.EventObject} e
9241          */
9242         "rowdblclick" : true,
9243         /**
9244          * @event mouseover
9245          * Fires when a mouseover occur
9246          * @param {Roo.bootstrap.Table} this
9247          * @param {Roo.Element} el
9248          * @param {Number} rowIndex
9249          * @param {Number} columnIndex
9250          * @param {Roo.EventObject} e
9251          */
9252         "mouseover" : true,
9253         /**
9254          * @event mouseout
9255          * Fires when a mouseout occur
9256          * @param {Roo.bootstrap.Table} this
9257          * @param {Roo.Element} el
9258          * @param {Number} rowIndex
9259          * @param {Number} columnIndex
9260          * @param {Roo.EventObject} e
9261          */
9262         "mouseout" : true,
9263         /**
9264          * @event rowclass
9265          * Fires when a row is rendered, so you can change add a style to it.
9266          * @param {Roo.bootstrap.Table} this
9267          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9268          */
9269         'rowclass' : true,
9270           /**
9271          * @event rowsrendered
9272          * Fires when all the  rows have been rendered
9273          * @param {Roo.bootstrap.Table} this
9274          */
9275         'rowsrendered' : true,
9276         /**
9277          * @event contextmenu
9278          * The raw contextmenu event for the entire grid.
9279          * @param {Roo.EventObject} e
9280          */
9281         "contextmenu" : true,
9282         /**
9283          * @event rowcontextmenu
9284          * Fires when a row is right clicked
9285          * @param {Roo.bootstrap.Table} this
9286          * @param {Number} rowIndex
9287          * @param {Roo.EventObject} e
9288          */
9289         "rowcontextmenu" : true,
9290         /**
9291          * @event cellcontextmenu
9292          * Fires when a cell is right clicked
9293          * @param {Roo.bootstrap.Table} this
9294          * @param {Number} rowIndex
9295          * @param {Number} cellIndex
9296          * @param {Roo.EventObject} e
9297          */
9298          "cellcontextmenu" : true,
9299          /**
9300          * @event headercontextmenu
9301          * Fires when a header is right clicked
9302          * @param {Roo.bootstrap.Table} this
9303          * @param {Number} columnIndex
9304          * @param {Roo.EventObject} e
9305          */
9306         "headercontextmenu" : true,
9307         /**
9308          * @event mousedown
9309          * The raw mousedown event for the entire grid.
9310          * @param {Roo.EventObject} e
9311          */
9312         "mousedown" : true
9313         
9314     });
9315 };
9316
9317 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9318     
9319     cls: false,
9320     
9321     empty_results : '',
9322     striped : false,
9323     scrollBody : false,
9324     bordered: false,
9325     hover:  false,
9326     condensed : false,
9327     responsive : false,
9328     sm : false,
9329     cm : false,
9330     store : false,
9331     loadMask : false,
9332     footerShow : true,
9333     headerShow : true,
9334     enableColumnResize: true,
9335   
9336     rowSelection : false,
9337     cellSelection : false,
9338     layout : false,
9339
9340     minColumnWidth : 50,
9341     
9342     // Roo.Element - the tbody
9343     bodyEl: false,  // <tbody> Roo.Element - thead element    
9344     headEl: false,  // <thead> Roo.Element - thead element
9345     resizeProxy : false, // proxy element for dragging?
9346
9347
9348     
9349     container: false, // used by gridpanel...
9350     
9351     lazyLoad : false,
9352     
9353     CSS : Roo.util.CSS,
9354     
9355     auto_hide_footer : false,
9356     
9357     view: false, // actually points to this..
9358     
9359     getAutoCreate : function()
9360     {
9361         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9362         
9363         cfg = {
9364             tag: 'table',
9365             cls : 'table', 
9366             cn : []
9367         };
9368         // this get's auto added by panel.Grid
9369         if (this.scrollBody) {
9370             cfg.cls += ' table-body-fixed';
9371         }    
9372         if (this.striped) {
9373             cfg.cls += ' table-striped';
9374         }
9375         
9376         if (this.hover) {
9377             cfg.cls += ' table-hover';
9378         }
9379         if (this.bordered) {
9380             cfg.cls += ' table-bordered';
9381         }
9382         if (this.condensed) {
9383             cfg.cls += ' table-condensed';
9384         }
9385         
9386         if (this.responsive) {
9387             cfg.cls += ' table-responsive';
9388         }
9389         
9390         if (this.cls) {
9391             cfg.cls+=  ' ' +this.cls;
9392         }
9393         
9394         
9395         
9396         if (this.layout) {
9397             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9398         }
9399         
9400         if(this.store || this.cm){
9401             if(this.headerShow){
9402                 cfg.cn.push(this.renderHeader());
9403             }
9404             
9405             cfg.cn.push(this.renderBody());
9406             
9407             if(this.footerShow){
9408                 cfg.cn.push(this.renderFooter());
9409             }
9410             // where does this come from?
9411             //cfg.cls+=  ' TableGrid';
9412         }
9413         
9414         return { cn : [ cfg ] };
9415     },
9416     
9417     initEvents : function()
9418     {   
9419         if(!this.store || !this.cm){
9420             return;
9421         }
9422         if (this.selModel) {
9423             this.selModel.initEvents();
9424         }
9425         
9426         
9427         //Roo.log('initEvents with ds!!!!');
9428         
9429         this.bodyEl = this.el.select('tbody', true).first();
9430         this.headEl = this.el.select('thead', true).first();
9431         this.mainFoot = this.el.select('tfoot', true).first();
9432         
9433         
9434         
9435         
9436         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9437             e.on('click', this.sort, this);
9438         }, this);
9439         
9440         
9441         // why is this done????? = it breaks dialogs??
9442         //this.parent().el.setStyle('position', 'relative');
9443         
9444         
9445         if (this.footer) {
9446             this.footer.parentId = this.id;
9447             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9448             
9449             if(this.lazyLoad){
9450                 this.el.select('tfoot tr td').first().addClass('hide');
9451             }
9452         } 
9453         
9454         if(this.loadMask) {
9455             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9456         }
9457         
9458         this.store.on('load', this.onLoad, this);
9459         this.store.on('beforeload', this.onBeforeLoad, this);
9460         this.store.on('update', this.onUpdate, this);
9461         this.store.on('add', this.onAdd, this);
9462         this.store.on("clear", this.clear, this);
9463         
9464         this.el.on("contextmenu", this.onContextMenu, this);
9465         
9466         
9467         this.cm.on("headerchange", this.onHeaderChange, this);
9468         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9469
9470  //?? does bodyEl get replaced on render?
9471         this.bodyEl.on("click", this.onClick, this);
9472         this.bodyEl.on("dblclick", this.onDblClick, this);        
9473         this.bodyEl.on('scroll', this.onBodyScroll, this);
9474
9475         // guessing mainbody will work - this relays usually caught by selmodel at present.
9476         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9477   
9478   
9479         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9480         
9481   
9482         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9483             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9484         }
9485         
9486         this.initCSS();
9487     },
9488     // Compatibility with grid - we implement all the view features at present.
9489     getView : function()
9490     {
9491         return this;
9492     },
9493     
9494     initCSS : function()
9495     {
9496         
9497         
9498         var cm = this.cm, styles = [];
9499         this.CSS.removeStyleSheet(this.id + '-cssrules');
9500         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9501         // we can honour xs/sm/md/xl  as widths...
9502         // we first have to decide what widht we are currently at...
9503         var sz = Roo.getGridSize();
9504         
9505         var total = 0;
9506         var last = -1;
9507         var cols = []; // visable cols.
9508         var total_abs = 0;
9509         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9510             var w = cm.getColumnWidth(i, false);
9511             if(cm.isHidden(i)){
9512                 cols.push( { rel : false, abs : 0 });
9513                 continue;
9514             }
9515             if (w !== false) {
9516                 cols.push( { rel : false, abs : w });
9517                 total_abs += w;
9518                 last = i; // not really..
9519                 continue;
9520             }
9521             var w = cm.getColumnWidth(i, sz);
9522             if (w > 0) {
9523                 last = i
9524             }
9525             total += w;
9526             cols.push( { rel : w, abs : false });
9527         }
9528         
9529         var avail = this.bodyEl.dom.clientWidth - total_abs;
9530         
9531         var unitWidth = Math.floor(avail / total);
9532         var rem = avail - (unitWidth * total);
9533         
9534         var hidden, width, pos = 0 , splithide , left;
9535         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9536             
9537             hidden = 'display:none;';
9538             left = '';
9539             width  = 'width:0px;';
9540             splithide = '';
9541             if(!cm.isHidden(i)){
9542                 hidden = '';
9543                 
9544                 
9545                 // we can honour xs/sm/md/xl ?
9546                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9547                 if (w===0) {
9548                     hidden = 'display:none;';
9549                 }
9550                 // width should return a small number...
9551                 if (i == last) {
9552                     w+=rem; // add the remaining with..
9553                 }
9554                 pos += w;
9555                 left = "left:" + (pos -4) + "px;";
9556                 width = "width:" + w+ "px;";
9557                 
9558             }
9559             if (this.responsive) {
9560                 width = '';
9561                 left = '';
9562                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9563                 splithide = 'display: none;';
9564             }
9565             
9566             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9567             if (this.headEl) {
9568                 if (i == last) {
9569                     splithide = 'display:none;';
9570                 }
9571                 
9572                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9573                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9574                             // this is the popover version..
9575                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9576                 );
9577             }
9578             
9579         }
9580         //Roo.log(styles.join(''));
9581         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9582         
9583     },
9584     
9585     
9586     
9587     onContextMenu : function(e, t)
9588     {
9589         this.processEvent("contextmenu", e);
9590     },
9591     
9592     processEvent : function(name, e)
9593     {
9594         if (name != 'touchstart' ) {
9595             this.fireEvent(name, e);    
9596         }
9597         
9598         var t = e.getTarget();
9599         
9600         var cell = Roo.get(t);
9601         
9602         if(!cell){
9603             return;
9604         }
9605         
9606         if(cell.findParent('tfoot', false, true)){
9607             return;
9608         }
9609         
9610         if(cell.findParent('thead', false, true)){
9611             
9612             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9613                 cell = Roo.get(t).findParent('th', false, true);
9614                 if (!cell) {
9615                     Roo.log("failed to find th in thead?");
9616                     Roo.log(e.getTarget());
9617                     return;
9618                 }
9619             }
9620             
9621             var cellIndex = cell.dom.cellIndex;
9622             
9623             var ename = name == 'touchstart' ? 'click' : name;
9624             this.fireEvent("header" + ename, this, cellIndex, e);
9625             
9626             return;
9627         }
9628         
9629         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9630             cell = Roo.get(t).findParent('td', false, true);
9631             if (!cell) {
9632                 Roo.log("failed to find th in tbody?");
9633                 Roo.log(e.getTarget());
9634                 return;
9635             }
9636         }
9637         
9638         var row = cell.findParent('tr', false, true);
9639         var cellIndex = cell.dom.cellIndex;
9640         var rowIndex = row.dom.rowIndex - 1;
9641         
9642         if(row !== false){
9643             
9644             this.fireEvent("row" + name, this, rowIndex, e);
9645             
9646             if(cell !== false){
9647             
9648                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9649             }
9650         }
9651         
9652     },
9653     
9654     onMouseover : function(e, el)
9655     {
9656         var cell = Roo.get(el);
9657         
9658         if(!cell){
9659             return;
9660         }
9661         
9662         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9663             cell = cell.findParent('td', false, true);
9664         }
9665         
9666         var row = cell.findParent('tr', false, true);
9667         var cellIndex = cell.dom.cellIndex;
9668         var rowIndex = row.dom.rowIndex - 1; // start from 0
9669         
9670         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9671         
9672     },
9673     
9674     onMouseout : function(e, el)
9675     {
9676         var cell = Roo.get(el);
9677         
9678         if(!cell){
9679             return;
9680         }
9681         
9682         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9683             cell = cell.findParent('td', false, true);
9684         }
9685         
9686         var row = cell.findParent('tr', false, true);
9687         var cellIndex = cell.dom.cellIndex;
9688         var rowIndex = row.dom.rowIndex - 1; // start from 0
9689         
9690         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9691         
9692     },
9693     
9694     onClick : function(e, el)
9695     {
9696         var cell = Roo.get(el);
9697         
9698         if(!cell || (!this.cellSelection && !this.rowSelection)){
9699             return;
9700         }
9701         
9702         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9703             cell = cell.findParent('td', false, true);
9704         }
9705         
9706         if(!cell || typeof(cell) == 'undefined'){
9707             return;
9708         }
9709         
9710         var row = cell.findParent('tr', false, true);
9711         
9712         if(!row || typeof(row) == 'undefined'){
9713             return;
9714         }
9715         
9716         var cellIndex = cell.dom.cellIndex;
9717         var rowIndex = this.getRowIndex(row);
9718         
9719         // why??? - should these not be based on SelectionModel?
9720         //if(this.cellSelection){
9721             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9722         //}
9723         
9724         //if(this.rowSelection){
9725             this.fireEvent('rowclick', this, row, rowIndex, e);
9726         //}
9727          
9728     },
9729         
9730     onDblClick : function(e,el)
9731     {
9732         var cell = Roo.get(el);
9733         
9734         if(!cell || (!this.cellSelection && !this.rowSelection)){
9735             return;
9736         }
9737         
9738         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9739             cell = cell.findParent('td', false, true);
9740         }
9741         
9742         if(!cell || typeof(cell) == 'undefined'){
9743             return;
9744         }
9745         
9746         var row = cell.findParent('tr', false, true);
9747         
9748         if(!row || typeof(row) == 'undefined'){
9749             return;
9750         }
9751         
9752         var cellIndex = cell.dom.cellIndex;
9753         var rowIndex = this.getRowIndex(row);
9754         
9755         if(this.cellSelection){
9756             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9757         }
9758         
9759         if(this.rowSelection){
9760             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9761         }
9762     },
9763     findRowIndex : function(el)
9764     {
9765         var cell = Roo.get(el);
9766         if(!cell) {
9767             return false;
9768         }
9769         var row = cell.findParent('tr', false, true);
9770         
9771         if(!row || typeof(row) == 'undefined'){
9772             return false;
9773         }
9774         return this.getRowIndex(row);
9775     },
9776     sort : function(e,el)
9777     {
9778         var col = Roo.get(el);
9779         
9780         if(!col.hasClass('sortable')){
9781             return;
9782         }
9783         
9784         var sort = col.attr('sort');
9785         var dir = 'ASC';
9786         
9787         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9788             dir = 'DESC';
9789         }
9790         
9791         this.store.sortInfo = {field : sort, direction : dir};
9792         
9793         if (this.footer) {
9794             Roo.log("calling footer first");
9795             this.footer.onClick('first');
9796         } else {
9797         
9798             this.store.load({ params : { start : 0 } });
9799         }
9800     },
9801     
9802     renderHeader : function()
9803     {
9804         var header = {
9805             tag: 'thead',
9806             cn : []
9807         };
9808         
9809         var cm = this.cm;
9810         this.totalWidth = 0;
9811         
9812         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9813             
9814             var config = cm.config[i];
9815             
9816             var c = {
9817                 tag: 'th',
9818                 cls : 'x-hcol-' + i,
9819                 style : '',
9820                 
9821                 html: cm.getColumnHeader(i)
9822             };
9823             
9824             var tooltip = cm.getColumnTooltip(i);
9825             if (tooltip) {
9826                 c.tooltip = tooltip;
9827             }
9828             
9829             
9830             var hh = '';
9831             
9832             if(typeof(config.sortable) != 'undefined' && config.sortable){
9833                 c.cls += ' sortable';
9834                 c.html = '<i class="fa"></i>' + c.html;
9835             }
9836             
9837             // could use BS4 hidden-..-down 
9838             
9839             if(typeof(config.lgHeader) != 'undefined'){
9840                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9841             }
9842             
9843             if(typeof(config.mdHeader) != 'undefined'){
9844                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9845             }
9846             
9847             if(typeof(config.smHeader) != 'undefined'){
9848                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9849             }
9850             
9851             if(typeof(config.xsHeader) != 'undefined'){
9852                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9853             }
9854             
9855             if(hh.length){
9856                 c.html = hh;
9857             }
9858             
9859             if(typeof(config.tooltip) != 'undefined'){
9860                 c.tooltip = config.tooltip;
9861             }
9862             
9863             if(typeof(config.colspan) != 'undefined'){
9864                 c.colspan = config.colspan;
9865             }
9866             
9867             // hidden is handled by CSS now
9868             
9869             if(typeof(config.dataIndex) != 'undefined'){
9870                 c.sort = config.dataIndex;
9871             }
9872             
9873            
9874             
9875             if(typeof(config.align) != 'undefined' && config.align.length){
9876                 c.style += ' text-align:' + config.align + ';';
9877             }
9878             
9879             /* width is done in CSS
9880              *if(typeof(config.width) != 'undefined'){
9881                 c.style += ' width:' + config.width + 'px;';
9882                 this.totalWidth += config.width;
9883             } else {
9884                 this.totalWidth += 100; // assume minimum of 100 per column?
9885             }
9886             */
9887             
9888             if(typeof(config.cls) != 'undefined'){
9889                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9890             }
9891             // this is the bit that doesnt reall work at all...
9892             
9893             if (this.responsive) {
9894                  
9895             
9896                 ['xs','sm','md','lg'].map(function(size){
9897                     
9898                     if(typeof(config[size]) == 'undefined'){
9899                         return;
9900                     }
9901                      
9902                     if (!config[size]) { // 0 = hidden
9903                         // BS 4 '0' is treated as hide that column and below.
9904                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9905                         return;
9906                     }
9907                     
9908                     c.cls += ' col-' + size + '-' + config[size] + (
9909                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9910                     );
9911                     
9912                     
9913                 });
9914             }
9915             // at the end?
9916             
9917             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9918             
9919             
9920             
9921             
9922             header.cn.push(c)
9923         }
9924         
9925         return header;
9926     },
9927     
9928     renderBody : function()
9929     {
9930         var body = {
9931             tag: 'tbody',
9932             cn : [
9933                 {
9934                     tag: 'tr',
9935                     cn : [
9936                         {
9937                             tag : 'td',
9938                             colspan :  this.cm.getColumnCount()
9939                         }
9940                     ]
9941                 }
9942             ]
9943         };
9944         
9945         return body;
9946     },
9947     
9948     renderFooter : function()
9949     {
9950         var footer = {
9951             tag: 'tfoot',
9952             cn : [
9953                 {
9954                     tag: 'tr',
9955                     cn : [
9956                         {
9957                             tag : 'td',
9958                             colspan :  this.cm.getColumnCount()
9959                         }
9960                     ]
9961                 }
9962             ]
9963         };
9964         
9965         return footer;
9966     },
9967     
9968     
9969     
9970     onLoad : function()
9971     {
9972 //        Roo.log('ds onload');
9973         this.clear();
9974         
9975         var _this = this;
9976         var cm = this.cm;
9977         var ds = this.store;
9978         
9979         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9980             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9981             if (_this.store.sortInfo) {
9982                     
9983                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9984                     e.select('i', true).addClass(['fa-arrow-up']);
9985                 }
9986                 
9987                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9988                     e.select('i', true).addClass(['fa-arrow-down']);
9989                 }
9990             }
9991         });
9992         
9993         var tbody =  this.bodyEl;
9994               
9995         if(ds.getCount() > 0){
9996             ds.data.each(function(d,rowIndex){
9997                 var row =  this.renderRow(cm, ds, rowIndex);
9998                 
9999                 tbody.createChild(row);
10000                 
10001                 var _this = this;
10002                 
10003                 if(row.cellObjects.length){
10004                     Roo.each(row.cellObjects, function(r){
10005                         _this.renderCellObject(r);
10006                     })
10007                 }
10008                 
10009             }, this);
10010         } else if (this.empty_results.length) {
10011             this.el.mask(this.empty_results, 'no-spinner');
10012         }
10013         
10014         var tfoot = this.el.select('tfoot', true).first();
10015         
10016         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10017             
10018             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10019             
10020             var total = this.ds.getTotalCount();
10021             
10022             if(this.footer.pageSize < total){
10023                 this.mainFoot.show();
10024             }
10025         }
10026         
10027         Roo.each(this.el.select('tbody td', true).elements, function(e){
10028             e.on('mouseover', _this.onMouseover, _this);
10029         });
10030         
10031         Roo.each(this.el.select('tbody td', true).elements, function(e){
10032             e.on('mouseout', _this.onMouseout, _this);
10033         });
10034         this.fireEvent('rowsrendered', this);
10035         
10036         this.autoSize();
10037         
10038         this.initCSS(); /// resize cols
10039
10040         
10041     },
10042     
10043     
10044     onUpdate : function(ds,record)
10045     {
10046         this.refreshRow(record);
10047         this.autoSize();
10048     },
10049     
10050     onRemove : function(ds, record, index, isUpdate){
10051         if(isUpdate !== true){
10052             this.fireEvent("beforerowremoved", this, index, record);
10053         }
10054         var bt = this.bodyEl.dom;
10055         
10056         var rows = this.el.select('tbody > tr', true).elements;
10057         
10058         if(typeof(rows[index]) != 'undefined'){
10059             bt.removeChild(rows[index].dom);
10060         }
10061         
10062 //        if(bt.rows[index]){
10063 //            bt.removeChild(bt.rows[index]);
10064 //        }
10065         
10066         if(isUpdate !== true){
10067             //this.stripeRows(index);
10068             //this.syncRowHeights(index, index);
10069             //this.layout();
10070             this.fireEvent("rowremoved", this, index, record);
10071         }
10072     },
10073     
10074     onAdd : function(ds, records, rowIndex)
10075     {
10076         //Roo.log('on Add called');
10077         // - note this does not handle multiple adding very well..
10078         var bt = this.bodyEl.dom;
10079         for (var i =0 ; i < records.length;i++) {
10080             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10081             //Roo.log(records[i]);
10082             //Roo.log(this.store.getAt(rowIndex+i));
10083             this.insertRow(this.store, rowIndex + i, false);
10084             return;
10085         }
10086         
10087     },
10088     
10089     
10090     refreshRow : function(record){
10091         var ds = this.store, index;
10092         if(typeof record == 'number'){
10093             index = record;
10094             record = ds.getAt(index);
10095         }else{
10096             index = ds.indexOf(record);
10097             if (index < 0) {
10098                 return; // should not happen - but seems to 
10099             }
10100         }
10101         this.insertRow(ds, index, true);
10102         this.autoSize();
10103         this.onRemove(ds, record, index+1, true);
10104         this.autoSize();
10105         //this.syncRowHeights(index, index);
10106         //this.layout();
10107         this.fireEvent("rowupdated", this, index, record);
10108     },
10109     // private - called by RowSelection
10110     onRowSelect : function(rowIndex){
10111         var row = this.getRowDom(rowIndex);
10112         row.addClass(['bg-info','info']);
10113     },
10114     // private - called by RowSelection
10115     onRowDeselect : function(rowIndex)
10116     {
10117         if (rowIndex < 0) {
10118             return;
10119         }
10120         var row = this.getRowDom(rowIndex);
10121         row.removeClass(['bg-info','info']);
10122     },
10123       /**
10124      * Focuses the specified row.
10125      * @param {Number} row The row index
10126      */
10127     focusRow : function(row)
10128     {
10129         //Roo.log('GridView.focusRow');
10130         var x = this.bodyEl.dom.scrollLeft;
10131         this.focusCell(row, 0, false);
10132         this.bodyEl.dom.scrollLeft = x;
10133
10134     },
10135      /**
10136      * Focuses the specified cell.
10137      * @param {Number} row The row index
10138      * @param {Number} col The column index
10139      * @param {Boolean} hscroll false to disable horizontal scrolling
10140      */
10141     focusCell : function(row, col, hscroll)
10142     {
10143         //Roo.log('GridView.focusCell');
10144         var el = this.ensureVisible(row, col, hscroll);
10145         // not sure what focusEL achives = it's a <a> pos relative 
10146         //this.focusEl.alignTo(el, "tl-tl");
10147         //if(Roo.isGecko){
10148         //    this.focusEl.focus();
10149         //}else{
10150         //    this.focusEl.focus.defer(1, this.focusEl);
10151         //}
10152     },
10153     
10154      /**
10155      * Scrolls the specified cell into view
10156      * @param {Number} row The row index
10157      * @param {Number} col The column index
10158      * @param {Boolean} hscroll false to disable horizontal scrolling
10159      */
10160     ensureVisible : function(row, col, hscroll)
10161     {
10162         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10163         //return null; //disable for testing.
10164         if(typeof row != "number"){
10165             row = row.rowIndex;
10166         }
10167         if(row < 0 && row >= this.ds.getCount()){
10168             return  null;
10169         }
10170         col = (col !== undefined ? col : 0);
10171         var cm = this.cm;
10172         while(cm.isHidden(col)){
10173             col++;
10174         }
10175
10176         var el = this.getCellDom(row, col);
10177         if(!el){
10178             return null;
10179         }
10180         var c = this.bodyEl.dom;
10181
10182         var ctop = parseInt(el.offsetTop, 10);
10183         var cleft = parseInt(el.offsetLeft, 10);
10184         var cbot = ctop + el.offsetHeight;
10185         var cright = cleft + el.offsetWidth;
10186
10187         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10188         var ch = 0; //?? header is not withing the area?
10189         var stop = parseInt(c.scrollTop, 10);
10190         var sleft = parseInt(c.scrollLeft, 10);
10191         var sbot = stop + ch;
10192         var sright = sleft + c.clientWidth;
10193         /*
10194         Roo.log('GridView.ensureVisible:' +
10195                 ' ctop:' + ctop +
10196                 ' c.clientHeight:' + c.clientHeight +
10197                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10198                 ' stop:' + stop +
10199                 ' cbot:' + cbot +
10200                 ' sbot:' + sbot +
10201                 ' ch:' + ch  
10202                 );
10203         */
10204         if(ctop < stop){
10205             c.scrollTop = ctop;
10206             //Roo.log("set scrolltop to ctop DISABLE?");
10207         }else if(cbot > sbot){
10208             //Roo.log("set scrolltop to cbot-ch");
10209             c.scrollTop = cbot-ch;
10210         }
10211
10212         if(hscroll !== false){
10213             if(cleft < sleft){
10214                 c.scrollLeft = cleft;
10215             }else if(cright > sright){
10216                 c.scrollLeft = cright-c.clientWidth;
10217             }
10218         }
10219
10220         return el;
10221     },
10222     
10223     
10224     insertRow : function(dm, rowIndex, isUpdate){
10225         
10226         if(!isUpdate){
10227             this.fireEvent("beforerowsinserted", this, rowIndex);
10228         }
10229             //var s = this.getScrollState();
10230         var row = this.renderRow(this.cm, this.store, rowIndex);
10231         // insert before rowIndex..
10232         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10233         
10234         var _this = this;
10235                 
10236         if(row.cellObjects.length){
10237             Roo.each(row.cellObjects, function(r){
10238                 _this.renderCellObject(r);
10239             })
10240         }
10241             
10242         if(!isUpdate){
10243             this.fireEvent("rowsinserted", this, rowIndex);
10244             //this.syncRowHeights(firstRow, lastRow);
10245             //this.stripeRows(firstRow);
10246             //this.layout();
10247         }
10248         
10249     },
10250     
10251     
10252     getRowDom : function(rowIndex)
10253     {
10254         var rows = this.el.select('tbody > tr', true).elements;
10255         
10256         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10257         
10258     },
10259     getCellDom : function(rowIndex, colIndex)
10260     {
10261         var row = this.getRowDom(rowIndex);
10262         if (row === false) {
10263             return false;
10264         }
10265         var cols = row.select('td', true).elements;
10266         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10267         
10268     },
10269     
10270     // returns the object tree for a tr..
10271   
10272     
10273     renderRow : function(cm, ds, rowIndex) 
10274     {
10275         var d = ds.getAt(rowIndex);
10276         
10277         var row = {
10278             tag : 'tr',
10279             cls : 'x-row-' + rowIndex,
10280             cn : []
10281         };
10282             
10283         var cellObjects = [];
10284         
10285         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10286             var config = cm.config[i];
10287             
10288             var renderer = cm.getRenderer(i);
10289             var value = '';
10290             var id = false;
10291             
10292             if(typeof(renderer) !== 'undefined'){
10293                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10294             }
10295             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10296             // and are rendered into the cells after the row is rendered - using the id for the element.
10297             
10298             if(typeof(value) === 'object'){
10299                 id = Roo.id();
10300                 cellObjects.push({
10301                     container : id,
10302                     cfg : value 
10303                 })
10304             }
10305             
10306             var rowcfg = {
10307                 record: d,
10308                 rowIndex : rowIndex,
10309                 colIndex : i,
10310                 rowClass : ''
10311             };
10312
10313             this.fireEvent('rowclass', this, rowcfg);
10314             
10315             var td = {
10316                 tag: 'td',
10317                 // this might end up displaying HTML?
10318                 // this is too messy... - better to only do it on columsn you know are going to be too long
10319                 //tooltip : (typeof(value) === 'object') ? '' : value,
10320                 cls : rowcfg.rowClass + ' x-col-' + i,
10321                 style: '',
10322                 html: (typeof(value) === 'object') ? '' : value
10323             };
10324             
10325             if (id) {
10326                 td.id = id;
10327             }
10328             
10329             if(typeof(config.colspan) != 'undefined'){
10330                 td.colspan = config.colspan;
10331             }
10332             
10333             
10334             
10335             if(typeof(config.align) != 'undefined' && config.align.length){
10336                 td.style += ' text-align:' + config.align + ';';
10337             }
10338             if(typeof(config.valign) != 'undefined' && config.valign.length){
10339                 td.style += ' vertical-align:' + config.valign + ';';
10340             }
10341             /*
10342             if(typeof(config.width) != 'undefined'){
10343                 td.style += ' width:' +  config.width + 'px;';
10344             }
10345             */
10346             
10347             if(typeof(config.cursor) != 'undefined'){
10348                 td.style += ' cursor:' +  config.cursor + ';';
10349             }
10350             
10351             if(typeof(config.cls) != 'undefined'){
10352                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10353             }
10354             if (this.responsive) {
10355                 ['xs','sm','md','lg'].map(function(size){
10356                     
10357                     if(typeof(config[size]) == 'undefined'){
10358                         return;
10359                     }
10360                     
10361                     
10362                       
10363                     if (!config[size]) { // 0 = hidden
10364                         // BS 4 '0' is treated as hide that column and below.
10365                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10366                         return;
10367                     }
10368                     
10369                     td.cls += ' col-' + size + '-' + config[size] + (
10370                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10371                     );
10372                      
10373     
10374                 });
10375             }
10376             row.cn.push(td);
10377            
10378         }
10379         
10380         row.cellObjects = cellObjects;
10381         
10382         return row;
10383           
10384     },
10385     
10386     
10387     
10388     onBeforeLoad : function()
10389     {
10390         this.el.unmask(); // if needed.
10391     },
10392      /**
10393      * Remove all rows
10394      */
10395     clear : function()
10396     {
10397         this.el.select('tbody', true).first().dom.innerHTML = '';
10398     },
10399     /**
10400      * Show or hide a row.
10401      * @param {Number} rowIndex to show or hide
10402      * @param {Boolean} state hide
10403      */
10404     setRowVisibility : function(rowIndex, state)
10405     {
10406         var bt = this.bodyEl.dom;
10407         
10408         var rows = this.el.select('tbody > tr', true).elements;
10409         
10410         if(typeof(rows[rowIndex]) == 'undefined'){
10411             return;
10412         }
10413         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10414         
10415     },
10416     
10417     
10418     getSelectionModel : function(){
10419         if(!this.selModel){
10420             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10421         }
10422         return this.selModel;
10423     },
10424     /*
10425      * Render the Roo.bootstrap object from renderder
10426      */
10427     renderCellObject : function(r)
10428     {
10429         var _this = this;
10430         
10431         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10432         
10433         var t = r.cfg.render(r.container);
10434         
10435         if(r.cfg.cn){
10436             Roo.each(r.cfg.cn, function(c){
10437                 var child = {
10438                     container: t.getChildContainer(),
10439                     cfg: c
10440                 };
10441                 _this.renderCellObject(child);
10442             })
10443         }
10444     },
10445     /**
10446      * get the Row Index from a dom element.
10447      * @param {Roo.Element} row The row to look for
10448      * @returns {Number} the row
10449      */
10450     getRowIndex : function(row)
10451     {
10452         var rowIndex = -1;
10453         
10454         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10455             if(el != row){
10456                 return;
10457             }
10458             
10459             rowIndex = index;
10460         });
10461         
10462         return rowIndex;
10463     },
10464     /**
10465      * get the header TH element for columnIndex
10466      * @param {Number} columnIndex
10467      * @returns {Roo.Element}
10468      */
10469     getHeaderIndex: function(colIndex)
10470     {
10471         var cols = this.headEl.select('th', true).elements;
10472         return cols[colIndex]; 
10473     },
10474     /**
10475      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10476      * @param {domElement} cell to look for
10477      * @returns {Number} the column
10478      */
10479     getCellIndex : function(cell)
10480     {
10481         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10482         if(id){
10483             return parseInt(id[1], 10);
10484         }
10485         return 0;
10486     },
10487      /**
10488      * Returns the grid's underlying element = used by panel.Grid
10489      * @return {Element} The element
10490      */
10491     getGridEl : function(){
10492         return this.el;
10493     },
10494      /**
10495      * Forces a resize - used by panel.Grid
10496      * @return {Element} The element
10497      */
10498     autoSize : function()
10499     {
10500         //var ctr = Roo.get(this.container.dom.parentElement);
10501         var ctr = Roo.get(this.el.dom);
10502         
10503         var thd = this.getGridEl().select('thead',true).first();
10504         var tbd = this.getGridEl().select('tbody', true).first();
10505         var tfd = this.getGridEl().select('tfoot', true).first();
10506         
10507         var cw = ctr.getWidth();
10508         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10509         
10510         if (tbd) {
10511             
10512             tbd.setWidth(ctr.getWidth());
10513             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10514             // this needs fixing for various usage - currently only hydra job advers I think..
10515             //tdb.setHeight(
10516             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10517             //); 
10518             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10519             cw -= barsize;
10520         }
10521         cw = Math.max(cw, this.totalWidth);
10522         this.getGridEl().select('tbody tr',true).setWidth(cw);
10523         this.initCSS();
10524         
10525         // resize 'expandable coloumn?
10526         
10527         return; // we doe not have a view in this design..
10528         
10529     },
10530     onBodyScroll: function()
10531     {
10532         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10533         if(this.headEl){
10534             this.headEl.setStyle({
10535                 'position' : 'relative',
10536                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10537             });
10538         }
10539         
10540         if(this.lazyLoad){
10541             
10542             var scrollHeight = this.bodyEl.dom.scrollHeight;
10543             
10544             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10545             
10546             var height = this.bodyEl.getHeight();
10547             
10548             if(scrollHeight - height == scrollTop) {
10549                 
10550                 var total = this.ds.getTotalCount();
10551                 
10552                 if(this.footer.cursor + this.footer.pageSize < total){
10553                     
10554                     this.footer.ds.load({
10555                         params : {
10556                             start : this.footer.cursor + this.footer.pageSize,
10557                             limit : this.footer.pageSize
10558                         },
10559                         add : true
10560                     });
10561                 }
10562             }
10563             
10564         }
10565     },
10566     onColumnSplitterMoved : function(i, diff)
10567     {
10568         this.userResized = true;
10569         
10570         var cm = this.colModel;
10571         
10572         var w = this.getHeaderIndex(i).getWidth() + diff;
10573         
10574         
10575         cm.setColumnWidth(i, w, true);
10576         this.initCSS();
10577         //var cid = cm.getColumnId(i); << not used in this version?
10578        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10579         
10580         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10581         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10582         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10583 */
10584         //this.updateSplitters();
10585         //this.layout(); << ??
10586         this.fireEvent("columnresize", i, w);
10587     },
10588     onHeaderChange : function()
10589     {
10590         var header = this.renderHeader();
10591         var table = this.el.select('table', true).first();
10592         
10593         this.headEl.remove();
10594         this.headEl = table.createChild(header, this.bodyEl, false);
10595         
10596         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10597             e.on('click', this.sort, this);
10598         }, this);
10599         
10600         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10601             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10602         }
10603         
10604     },
10605     
10606     onHiddenChange : function(colModel, colIndex, hidden)
10607     {
10608         /*
10609         this.cm.setHidden()
10610         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10611         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10612         
10613         this.CSS.updateRule(thSelector, "display", "");
10614         this.CSS.updateRule(tdSelector, "display", "");
10615         
10616         if(hidden){
10617             this.CSS.updateRule(thSelector, "display", "none");
10618             this.CSS.updateRule(tdSelector, "display", "none");
10619         }
10620         */
10621         // onload calls initCSS()
10622         this.onHeaderChange();
10623         this.onLoad();
10624     },
10625     
10626     setColumnWidth: function(col_index, width)
10627     {
10628         // width = "md-2 xs-2..."
10629         if(!this.colModel.config[col_index]) {
10630             return;
10631         }
10632         
10633         var w = width.split(" ");
10634         
10635         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10636         
10637         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10638         
10639         
10640         for(var j = 0; j < w.length; j++) {
10641             
10642             if(!w[j]) {
10643                 continue;
10644             }
10645             
10646             var size_cls = w[j].split("-");
10647             
10648             if(!Number.isInteger(size_cls[1] * 1)) {
10649                 continue;
10650             }
10651             
10652             if(!this.colModel.config[col_index][size_cls[0]]) {
10653                 continue;
10654             }
10655             
10656             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10657                 continue;
10658             }
10659             
10660             h_row[0].classList.replace(
10661                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10662                 "col-"+size_cls[0]+"-"+size_cls[1]
10663             );
10664             
10665             for(var i = 0; i < rows.length; i++) {
10666                 
10667                 var size_cls = w[j].split("-");
10668                 
10669                 if(!Number.isInteger(size_cls[1] * 1)) {
10670                     continue;
10671                 }
10672                 
10673                 if(!this.colModel.config[col_index][size_cls[0]]) {
10674                     continue;
10675                 }
10676                 
10677                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10678                     continue;
10679                 }
10680                 
10681                 rows[i].classList.replace(
10682                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10683                     "col-"+size_cls[0]+"-"+size_cls[1]
10684                 );
10685             }
10686             
10687             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10688         }
10689     }
10690 });
10691
10692 // currently only used to find the split on drag.. 
10693 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10694
10695 /**
10696  * @depricated
10697 */
10698 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10699 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10700 /*
10701  * - LGPL
10702  *
10703  * table cell
10704  * 
10705  */
10706
10707 /**
10708  * @class Roo.bootstrap.TableCell
10709  * @extends Roo.bootstrap.Component
10710  * @children Roo.bootstrap.Component
10711  * @parent Roo.bootstrap.TableRow
10712  * Bootstrap TableCell class
10713  * 
10714  * @cfg {String} html cell contain text
10715  * @cfg {String} cls cell class
10716  * @cfg {String} tag cell tag (td|th) default td
10717  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10718  * @cfg {String} align Aligns the content in a cell
10719  * @cfg {String} axis Categorizes cells
10720  * @cfg {String} bgcolor Specifies the background color of a cell
10721  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10722  * @cfg {Number} colspan Specifies the number of columns a cell should span
10723  * @cfg {String} headers Specifies one or more header cells a cell is related to
10724  * @cfg {Number} height Sets the height of a cell
10725  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10726  * @cfg {Number} rowspan Sets the number of rows a cell should span
10727  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10728  * @cfg {String} valign Vertical aligns the content in a cell
10729  * @cfg {Number} width Specifies the width of a cell
10730  * 
10731  * @constructor
10732  * Create a new TableCell
10733  * @param {Object} config The config object
10734  */
10735
10736 Roo.bootstrap.TableCell = function(config){
10737     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10738 };
10739
10740 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10741     
10742     html: false,
10743     cls: false,
10744     tag: false,
10745     abbr: false,
10746     align: false,
10747     axis: false,
10748     bgcolor: false,
10749     charoff: false,
10750     colspan: false,
10751     headers: false,
10752     height: false,
10753     nowrap: false,
10754     rowspan: false,
10755     scope: false,
10756     valign: false,
10757     width: false,
10758     
10759     
10760     getAutoCreate : function(){
10761         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10762         
10763         cfg = {
10764             tag: 'td'
10765         };
10766         
10767         if(this.tag){
10768             cfg.tag = this.tag;
10769         }
10770         
10771         if (this.html) {
10772             cfg.html=this.html
10773         }
10774         if (this.cls) {
10775             cfg.cls=this.cls
10776         }
10777         if (this.abbr) {
10778             cfg.abbr=this.abbr
10779         }
10780         if (this.align) {
10781             cfg.align=this.align
10782         }
10783         if (this.axis) {
10784             cfg.axis=this.axis
10785         }
10786         if (this.bgcolor) {
10787             cfg.bgcolor=this.bgcolor
10788         }
10789         if (this.charoff) {
10790             cfg.charoff=this.charoff
10791         }
10792         if (this.colspan) {
10793             cfg.colspan=this.colspan
10794         }
10795         if (this.headers) {
10796             cfg.headers=this.headers
10797         }
10798         if (this.height) {
10799             cfg.height=this.height
10800         }
10801         if (this.nowrap) {
10802             cfg.nowrap=this.nowrap
10803         }
10804         if (this.rowspan) {
10805             cfg.rowspan=this.rowspan
10806         }
10807         if (this.scope) {
10808             cfg.scope=this.scope
10809         }
10810         if (this.valign) {
10811             cfg.valign=this.valign
10812         }
10813         if (this.width) {
10814             cfg.width=this.width
10815         }
10816         
10817         
10818         return cfg;
10819     }
10820    
10821 });
10822
10823  
10824
10825  /*
10826  * - LGPL
10827  *
10828  * table row
10829  * 
10830  */
10831
10832 /**
10833  * @class Roo.bootstrap.TableRow
10834  * @extends Roo.bootstrap.Component
10835  * @children Roo.bootstrap.TableCell
10836  * @parent Roo.bootstrap.TableBody
10837  * Bootstrap TableRow class
10838  * @cfg {String} cls row class
10839  * @cfg {String} align Aligns the content in a table row
10840  * @cfg {String} bgcolor Specifies a background color for a table row
10841  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10842  * @cfg {String} valign Vertical aligns the content in a table row
10843  * 
10844  * @constructor
10845  * Create a new TableRow
10846  * @param {Object} config The config object
10847  */
10848
10849 Roo.bootstrap.TableRow = function(config){
10850     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10851 };
10852
10853 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10854     
10855     cls: false,
10856     align: false,
10857     bgcolor: false,
10858     charoff: false,
10859     valign: false,
10860     
10861     getAutoCreate : function(){
10862         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10863         
10864         cfg = {
10865             tag: 'tr'
10866         };
10867             
10868         if(this.cls){
10869             cfg.cls = this.cls;
10870         }
10871         if(this.align){
10872             cfg.align = this.align;
10873         }
10874         if(this.bgcolor){
10875             cfg.bgcolor = this.bgcolor;
10876         }
10877         if(this.charoff){
10878             cfg.charoff = this.charoff;
10879         }
10880         if(this.valign){
10881             cfg.valign = this.valign;
10882         }
10883         
10884         return cfg;
10885     }
10886    
10887 });
10888
10889  
10890
10891  /*
10892  * - LGPL
10893  *
10894  * table body
10895  * 
10896  */
10897
10898 /**
10899  * @class Roo.bootstrap.TableBody
10900  * @extends Roo.bootstrap.Component
10901  * @children Roo.bootstrap.TableRow
10902  * @parent Roo.bootstrap.Table
10903  * Bootstrap TableBody class
10904  * @cfg {String} cls element class
10905  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10906  * @cfg {String} align Aligns the content inside the element
10907  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10908  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10909  * 
10910  * @constructor
10911  * Create a new TableBody
10912  * @param {Object} config The config object
10913  */
10914
10915 Roo.bootstrap.TableBody = function(config){
10916     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10917 };
10918
10919 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10920     
10921     cls: false,
10922     tag: false,
10923     align: false,
10924     charoff: false,
10925     valign: false,
10926     
10927     getAutoCreate : function(){
10928         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10929         
10930         cfg = {
10931             tag: 'tbody'
10932         };
10933             
10934         if (this.cls) {
10935             cfg.cls=this.cls
10936         }
10937         if(this.tag){
10938             cfg.tag = this.tag;
10939         }
10940         
10941         if(this.align){
10942             cfg.align = this.align;
10943         }
10944         if(this.charoff){
10945             cfg.charoff = this.charoff;
10946         }
10947         if(this.valign){
10948             cfg.valign = this.valign;
10949         }
10950         
10951         return cfg;
10952     }
10953     
10954     
10955 //    initEvents : function()
10956 //    {
10957 //        
10958 //        if(!this.store){
10959 //            return;
10960 //        }
10961 //        
10962 //        this.store = Roo.factory(this.store, Roo.data);
10963 //        this.store.on('load', this.onLoad, this);
10964 //        
10965 //        this.store.load();
10966 //        
10967 //    },
10968 //    
10969 //    onLoad: function () 
10970 //    {   
10971 //        this.fireEvent('load', this);
10972 //    }
10973 //    
10974 //   
10975 });
10976
10977  
10978
10979  /*
10980  * Based on:
10981  * Ext JS Library 1.1.1
10982  * Copyright(c) 2006-2007, Ext JS, LLC.
10983  *
10984  * Originally Released Under LGPL - original licence link has changed is not relivant.
10985  *
10986  * Fork - LGPL
10987  * <script type="text/javascript">
10988  */
10989
10990 // as we use this in bootstrap.
10991 Roo.namespace('Roo.form');
10992  /**
10993  * @class Roo.form.Action
10994  * Internal Class used to handle form actions
10995  * @constructor
10996  * @param {Roo.form.BasicForm} el The form element or its id
10997  * @param {Object} config Configuration options
10998  */
10999
11000  
11001  
11002 // define the action interface
11003 Roo.form.Action = function(form, options){
11004     this.form = form;
11005     this.options = options || {};
11006 };
11007 /**
11008  * Client Validation Failed
11009  * @const 
11010  */
11011 Roo.form.Action.CLIENT_INVALID = 'client';
11012 /**
11013  * Server Validation Failed
11014  * @const 
11015  */
11016 Roo.form.Action.SERVER_INVALID = 'server';
11017  /**
11018  * Connect to Server Failed
11019  * @const 
11020  */
11021 Roo.form.Action.CONNECT_FAILURE = 'connect';
11022 /**
11023  * Reading Data from Server Failed
11024  * @const 
11025  */
11026 Roo.form.Action.LOAD_FAILURE = 'load';
11027
11028 Roo.form.Action.prototype = {
11029     type : 'default',
11030     failureType : undefined,
11031     response : undefined,
11032     result : undefined,
11033
11034     // interface method
11035     run : function(options){
11036
11037     },
11038
11039     // interface method
11040     success : function(response){
11041
11042     },
11043
11044     // interface method
11045     handleResponse : function(response){
11046
11047     },
11048
11049     // default connection failure
11050     failure : function(response){
11051         
11052         this.response = response;
11053         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11054         this.form.afterAction(this, false);
11055     },
11056
11057     processResponse : function(response){
11058         this.response = response;
11059         if(!response.responseText){
11060             return true;
11061         }
11062         this.result = this.handleResponse(response);
11063         return this.result;
11064     },
11065
11066     // utility functions used internally
11067     getUrl : function(appendParams){
11068         var url = this.options.url || this.form.url || this.form.el.dom.action;
11069         if(appendParams){
11070             var p = this.getParams();
11071             if(p){
11072                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11073             }
11074         }
11075         return url;
11076     },
11077
11078     getMethod : function(){
11079         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11080     },
11081
11082     getParams : function(){
11083         var bp = this.form.baseParams;
11084         var p = this.options.params;
11085         if(p){
11086             if(typeof p == "object"){
11087                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11088             }else if(typeof p == 'string' && bp){
11089                 p += '&' + Roo.urlEncode(bp);
11090             }
11091         }else if(bp){
11092             p = Roo.urlEncode(bp);
11093         }
11094         return p;
11095     },
11096
11097     createCallback : function(){
11098         return {
11099             success: this.success,
11100             failure: this.failure,
11101             scope: this,
11102             timeout: (this.form.timeout*1000),
11103             upload: this.form.fileUpload ? this.success : undefined
11104         };
11105     }
11106 };
11107
11108 Roo.form.Action.Submit = function(form, options){
11109     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11110 };
11111
11112 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11113     type : 'submit',
11114
11115     haveProgress : false,
11116     uploadComplete : false,
11117     
11118     // uploadProgress indicator.
11119     uploadProgress : function()
11120     {
11121         if (!this.form.progressUrl) {
11122             return;
11123         }
11124         
11125         if (!this.haveProgress) {
11126             Roo.MessageBox.progress("Uploading", "Uploading");
11127         }
11128         if (this.uploadComplete) {
11129            Roo.MessageBox.hide();
11130            return;
11131         }
11132         
11133         this.haveProgress = true;
11134    
11135         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11136         
11137         var c = new Roo.data.Connection();
11138         c.request({
11139             url : this.form.progressUrl,
11140             params: {
11141                 id : uid
11142             },
11143             method: 'GET',
11144             success : function(req){
11145                //console.log(data);
11146                 var rdata = false;
11147                 var edata;
11148                 try  {
11149                    rdata = Roo.decode(req.responseText)
11150                 } catch (e) {
11151                     Roo.log("Invalid data from server..");
11152                     Roo.log(edata);
11153                     return;
11154                 }
11155                 if (!rdata || !rdata.success) {
11156                     Roo.log(rdata);
11157                     Roo.MessageBox.alert(Roo.encode(rdata));
11158                     return;
11159                 }
11160                 var data = rdata.data;
11161                 
11162                 if (this.uploadComplete) {
11163                    Roo.MessageBox.hide();
11164                    return;
11165                 }
11166                    
11167                 if (data){
11168                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11169                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11170                     );
11171                 }
11172                 this.uploadProgress.defer(2000,this);
11173             },
11174        
11175             failure: function(data) {
11176                 Roo.log('progress url failed ');
11177                 Roo.log(data);
11178             },
11179             scope : this
11180         });
11181            
11182     },
11183     
11184     
11185     run : function()
11186     {
11187         // run get Values on the form, so it syncs any secondary forms.
11188         this.form.getValues();
11189         
11190         var o = this.options;
11191         var method = this.getMethod();
11192         var isPost = method == 'POST';
11193         if(o.clientValidation === false || this.form.isValid()){
11194             
11195             if (this.form.progressUrl) {
11196                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11197                     (new Date() * 1) + '' + Math.random());
11198                     
11199             } 
11200             
11201             
11202             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11203                 form:this.form.el.dom,
11204                 url:this.getUrl(!isPost),
11205                 method: method,
11206                 params:isPost ? this.getParams() : null,
11207                 isUpload: this.form.fileUpload,
11208                 formData : this.form.formData
11209             }));
11210             
11211             this.uploadProgress();
11212
11213         }else if (o.clientValidation !== false){ // client validation failed
11214             this.failureType = Roo.form.Action.CLIENT_INVALID;
11215             this.form.afterAction(this, false);
11216         }
11217     },
11218
11219     success : function(response)
11220     {
11221         this.uploadComplete= true;
11222         if (this.haveProgress) {
11223             Roo.MessageBox.hide();
11224         }
11225         
11226         
11227         var result = this.processResponse(response);
11228         if(result === true || result.success){
11229             this.form.afterAction(this, true);
11230             return;
11231         }
11232         if(result.errors){
11233             this.form.markInvalid(result.errors);
11234             this.failureType = Roo.form.Action.SERVER_INVALID;
11235         }
11236         this.form.afterAction(this, false);
11237     },
11238     failure : function(response)
11239     {
11240         this.uploadComplete= true;
11241         if (this.haveProgress) {
11242             Roo.MessageBox.hide();
11243         }
11244         
11245         this.response = response;
11246         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11247         this.form.afterAction(this, false);
11248     },
11249     
11250     handleResponse : function(response){
11251         if(this.form.errorReader){
11252             var rs = this.form.errorReader.read(response);
11253             var errors = [];
11254             if(rs.records){
11255                 for(var i = 0, len = rs.records.length; i < len; i++) {
11256                     var r = rs.records[i];
11257                     errors[i] = r.data;
11258                 }
11259             }
11260             if(errors.length < 1){
11261                 errors = null;
11262             }
11263             return {
11264                 success : rs.success,
11265                 errors : errors
11266             };
11267         }
11268         var ret = false;
11269         try {
11270             ret = Roo.decode(response.responseText);
11271         } catch (e) {
11272             ret = {
11273                 success: false,
11274                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11275                 errors : []
11276             };
11277         }
11278         return ret;
11279         
11280     }
11281 });
11282
11283
11284 Roo.form.Action.Load = function(form, options){
11285     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11286     this.reader = this.form.reader;
11287 };
11288
11289 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11290     type : 'load',
11291
11292     run : function(){
11293         
11294         Roo.Ajax.request(Roo.apply(
11295                 this.createCallback(), {
11296                     method:this.getMethod(),
11297                     url:this.getUrl(false),
11298                     params:this.getParams()
11299         }));
11300     },
11301
11302     success : function(response){
11303         
11304         var result = this.processResponse(response);
11305         if(result === true || !result.success || !result.data){
11306             this.failureType = Roo.form.Action.LOAD_FAILURE;
11307             this.form.afterAction(this, false);
11308             return;
11309         }
11310         this.form.clearInvalid();
11311         this.form.setValues(result.data);
11312         this.form.afterAction(this, true);
11313     },
11314
11315     handleResponse : function(response){
11316         if(this.form.reader){
11317             var rs = this.form.reader.read(response);
11318             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11319             return {
11320                 success : rs.success,
11321                 data : data
11322             };
11323         }
11324         return Roo.decode(response.responseText);
11325     }
11326 });
11327
11328 Roo.form.Action.ACTION_TYPES = {
11329     'load' : Roo.form.Action.Load,
11330     'submit' : Roo.form.Action.Submit
11331 };/*
11332  * - LGPL
11333  *
11334  * form
11335  *
11336  */
11337
11338 /**
11339  * @class Roo.bootstrap.form.Form
11340  * @extends Roo.bootstrap.Component
11341  * @children Roo.bootstrap.Component
11342  * Bootstrap Form class
11343  * @cfg {String} method  GET | POST (default POST)
11344  * @cfg {String} labelAlign top | left (default top)
11345  * @cfg {String} align left  | right - for navbars
11346  * @cfg {Boolean} loadMask load mask when submit (default true)
11347
11348  *
11349  * @constructor
11350  * Create a new Form
11351  * @param {Object} config The config object
11352  */
11353
11354
11355 Roo.bootstrap.form.Form = function(config){
11356     
11357     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11358     
11359     Roo.bootstrap.form.Form.popover.apply();
11360     
11361     this.addEvents({
11362         /**
11363          * @event clientvalidation
11364          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11365          * @param {Form} this
11366          * @param {Boolean} valid true if the form has passed client-side validation
11367          */
11368         clientvalidation: true,
11369         /**
11370          * @event beforeaction
11371          * Fires before any action is performed. Return false to cancel the action.
11372          * @param {Form} this
11373          * @param {Action} action The action to be performed
11374          */
11375         beforeaction: true,
11376         /**
11377          * @event actionfailed
11378          * Fires when an action fails.
11379          * @param {Form} this
11380          * @param {Action} action The action that failed
11381          */
11382         actionfailed : true,
11383         /**
11384          * @event actioncomplete
11385          * Fires when an action is completed.
11386          * @param {Form} this
11387          * @param {Action} action The action that completed
11388          */
11389         actioncomplete : true
11390     });
11391 };
11392
11393 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11394
11395      /**
11396      * @cfg {String} method
11397      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11398      */
11399     method : 'POST',
11400     /**
11401      * @cfg {String} url
11402      * The URL to use for form actions if one isn't supplied in the action options.
11403      */
11404     /**
11405      * @cfg {Boolean} fileUpload
11406      * Set to true if this form is a file upload.
11407      */
11408
11409     /**
11410      * @cfg {Object} baseParams
11411      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11412      */
11413
11414     /**
11415      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11416      */
11417     timeout: 30,
11418     /**
11419      * @cfg {Sting} align (left|right) for navbar forms
11420      */
11421     align : 'left',
11422
11423     // private
11424     activeAction : null,
11425
11426     /**
11427      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11428      * element by passing it or its id or mask the form itself by passing in true.
11429      * @type Mixed
11430      */
11431     waitMsgTarget : false,
11432
11433     loadMask : true,
11434     
11435     /**
11436      * @cfg {Boolean} errorMask (true|false) default false
11437      */
11438     errorMask : false,
11439     
11440     /**
11441      * @cfg {Number} maskOffset Default 100
11442      */
11443     maskOffset : 100,
11444     
11445     /**
11446      * @cfg {Boolean} maskBody
11447      */
11448     maskBody : false,
11449
11450     getAutoCreate : function(){
11451
11452         var cfg = {
11453             tag: 'form',
11454             method : this.method || 'POST',
11455             id : this.id || Roo.id(),
11456             cls : ''
11457         };
11458         if (this.parent().xtype.match(/^Nav/)) {
11459             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11460
11461         }
11462
11463         if (this.labelAlign == 'left' ) {
11464             cfg.cls += ' form-horizontal';
11465         }
11466
11467
11468         return cfg;
11469     },
11470     initEvents : function()
11471     {
11472         this.el.on('submit', this.onSubmit, this);
11473         // this was added as random key presses on the form where triggering form submit.
11474         this.el.on('keypress', function(e) {
11475             if (e.getCharCode() != 13) {
11476                 return true;
11477             }
11478             // we might need to allow it for textareas.. and some other items.
11479             // check e.getTarget().
11480
11481             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11482                 return true;
11483             }
11484
11485             Roo.log("keypress blocked");
11486
11487             e.preventDefault();
11488             return false;
11489         });
11490         
11491     },
11492     // private
11493     onSubmit : function(e){
11494         e.stopEvent();
11495     },
11496
11497      /**
11498      * Returns true if client-side validation on the form is successful.
11499      * @return Boolean
11500      */
11501     isValid : function(){
11502         var items = this.getItems();
11503         var valid = true;
11504         var target = false;
11505         
11506         items.each(function(f){
11507             
11508             if(f.validate()){
11509                 return;
11510             }
11511             
11512             Roo.log('invalid field: ' + f.name);
11513             
11514             valid = false;
11515
11516             if(!target && f.el.isVisible(true)){
11517                 target = f;
11518             }
11519            
11520         });
11521         
11522         if(this.errorMask && !valid){
11523             Roo.bootstrap.form.Form.popover.mask(this, target);
11524         }
11525         
11526         return valid;
11527     },
11528     
11529     /**
11530      * Returns true if any fields in this form have changed since their original load.
11531      * @return Boolean
11532      */
11533     isDirty : function(){
11534         var dirty = false;
11535         var items = this.getItems();
11536         items.each(function(f){
11537            if(f.isDirty()){
11538                dirty = true;
11539                return false;
11540            }
11541            return true;
11542         });
11543         return dirty;
11544     },
11545      /**
11546      * Performs a predefined action (submit or load) or custom actions you define on this form.
11547      * @param {String} actionName The name of the action type
11548      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11549      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11550      * accept other config options):
11551      * <pre>
11552 Property          Type             Description
11553 ----------------  ---------------  ----------------------------------------------------------------------------------
11554 url               String           The url for the action (defaults to the form's url)
11555 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11556 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11557 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11558                                    validate the form on the client (defaults to false)
11559      * </pre>
11560      * @return {BasicForm} this
11561      */
11562     doAction : function(action, options){
11563         if(typeof action == 'string'){
11564             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11565         }
11566         if(this.fireEvent('beforeaction', this, action) !== false){
11567             this.beforeAction(action);
11568             action.run.defer(100, action);
11569         }
11570         return this;
11571     },
11572
11573     // private
11574     beforeAction : function(action){
11575         var o = action.options;
11576         
11577         if(this.loadMask){
11578             
11579             if(this.maskBody){
11580                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11581             } else {
11582                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11583             }
11584         }
11585         // not really supported yet.. ??
11586
11587         //if(this.waitMsgTarget === true){
11588         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11589         //}else if(this.waitMsgTarget){
11590         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11591         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11592         //}else {
11593         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11594        // }
11595
11596     },
11597
11598     // private
11599     afterAction : function(action, success){
11600         this.activeAction = null;
11601         var o = action.options;
11602
11603         if(this.loadMask){
11604             
11605             if(this.maskBody){
11606                 Roo.get(document.body).unmask();
11607             } else {
11608                 this.el.unmask();
11609             }
11610         }
11611         
11612         //if(this.waitMsgTarget === true){
11613 //            this.el.unmask();
11614         //}else if(this.waitMsgTarget){
11615         //    this.waitMsgTarget.unmask();
11616         //}else{
11617         //    Roo.MessageBox.updateProgress(1);
11618         //    Roo.MessageBox.hide();
11619        // }
11620         //
11621         if(success){
11622             if(o.reset){
11623                 this.reset();
11624             }
11625             Roo.callback(o.success, o.scope, [this, action]);
11626             this.fireEvent('actioncomplete', this, action);
11627
11628         }else{
11629
11630             // failure condition..
11631             // we have a scenario where updates need confirming.
11632             // eg. if a locking scenario exists..
11633             // we look for { errors : { needs_confirm : true }} in the response.
11634             if (
11635                 (typeof(action.result) != 'undefined')  &&
11636                 (typeof(action.result.errors) != 'undefined')  &&
11637                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11638            ){
11639                 var _t = this;
11640                 Roo.log("not supported yet");
11641                  /*
11642
11643                 Roo.MessageBox.confirm(
11644                     "Change requires confirmation",
11645                     action.result.errorMsg,
11646                     function(r) {
11647                         if (r != 'yes') {
11648                             return;
11649                         }
11650                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11651                     }
11652
11653                 );
11654                 */
11655
11656
11657                 return;
11658             }
11659
11660             Roo.callback(o.failure, o.scope, [this, action]);
11661             // show an error message if no failed handler is set..
11662             if (!this.hasListener('actionfailed')) {
11663                 Roo.log("need to add dialog support");
11664                 /*
11665                 Roo.MessageBox.alert("Error",
11666                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11667                         action.result.errorMsg :
11668                         "Saving Failed, please check your entries or try again"
11669                 );
11670                 */
11671             }
11672
11673             this.fireEvent('actionfailed', this, action);
11674         }
11675
11676     },
11677     /**
11678      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11679      * @param {String} id The value to search for
11680      * @return Field
11681      */
11682     findField : function(id){
11683         var items = this.getItems();
11684         var field = items.get(id);
11685         if(!field){
11686              items.each(function(f){
11687                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11688                     field = f;
11689                     return false;
11690                 }
11691                 return true;
11692             });
11693         }
11694         return field || null;
11695     },
11696      /**
11697      * Mark fields in this form invalid in bulk.
11698      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11699      * @return {BasicForm} this
11700      */
11701     markInvalid : function(errors){
11702         if(errors instanceof Array){
11703             for(var i = 0, len = errors.length; i < len; i++){
11704                 var fieldError = errors[i];
11705                 var f = this.findField(fieldError.id);
11706                 if(f){
11707                     f.markInvalid(fieldError.msg);
11708                 }
11709             }
11710         }else{
11711             var field, id;
11712             for(id in errors){
11713                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11714                     field.markInvalid(errors[id]);
11715                 }
11716             }
11717         }
11718         //Roo.each(this.childForms || [], function (f) {
11719         //    f.markInvalid(errors);
11720         //});
11721
11722         return this;
11723     },
11724
11725     /**
11726      * Set values for fields in this form in bulk.
11727      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11728      * @return {BasicForm} this
11729      */
11730     setValues : function(values){
11731         if(values instanceof Array){ // array of objects
11732             for(var i = 0, len = values.length; i < len; i++){
11733                 var v = values[i];
11734                 var f = this.findField(v.id);
11735                 if(f){
11736                     f.setValue(v.value);
11737                     if(this.trackResetOnLoad){
11738                         f.originalValue = f.getValue();
11739                     }
11740                 }
11741             }
11742         }else{ // object hash
11743             var field, id;
11744             for(id in values){
11745                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11746
11747                     if (field.setFromData &&
11748                         field.valueField &&
11749                         field.displayField &&
11750                         // combos' with local stores can
11751                         // be queried via setValue()
11752                         // to set their value..
11753                         (field.store && !field.store.isLocal)
11754                         ) {
11755                         // it's a combo
11756                         var sd = { };
11757                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11758                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11759                         field.setFromData(sd);
11760
11761                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11762                         
11763                         field.setFromData(values);
11764                         
11765                     } else {
11766                         field.setValue(values[id]);
11767                     }
11768
11769
11770                     if(this.trackResetOnLoad){
11771                         field.originalValue = field.getValue();
11772                     }
11773                 }
11774             }
11775         }
11776
11777         //Roo.each(this.childForms || [], function (f) {
11778         //    f.setValues(values);
11779         //});
11780
11781         return this;
11782     },
11783
11784     /**
11785      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11786      * they are returned as an array.
11787      * @param {Boolean} asString
11788      * @return {Object}
11789      */
11790     getValues : function(asString){
11791         //if (this.childForms) {
11792             // copy values from the child forms
11793         //    Roo.each(this.childForms, function (f) {
11794         //        this.setValues(f.getValues());
11795         //    }, this);
11796         //}
11797
11798
11799
11800         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11801         if(asString === true){
11802             return fs;
11803         }
11804         return Roo.urlDecode(fs);
11805     },
11806
11807     /**
11808      * Returns the fields in this form as an object with key/value pairs.
11809      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11810      * @return {Object}
11811      */
11812     getFieldValues : function(with_hidden)
11813     {
11814         var items = this.getItems();
11815         var ret = {};
11816         items.each(function(f){
11817             
11818             if (!f.getName()) {
11819                 return;
11820             }
11821             
11822             var v = f.getValue();
11823             
11824             if (f.inputType =='radio') {
11825                 if (typeof(ret[f.getName()]) == 'undefined') {
11826                     ret[f.getName()] = ''; // empty..
11827                 }
11828
11829                 if (!f.el.dom.checked) {
11830                     return;
11831
11832                 }
11833                 v = f.el.dom.value;
11834
11835             }
11836             
11837             if(f.xtype == 'MoneyField'){
11838                 ret[f.currencyName] = f.getCurrency();
11839             }
11840
11841             // not sure if this supported any more..
11842             if ((typeof(v) == 'object') && f.getRawValue) {
11843                 v = f.getRawValue() ; // dates..
11844             }
11845             // combo boxes where name != hiddenName...
11846             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11847                 ret[f.name] = f.getRawValue();
11848             }
11849             ret[f.getName()] = v;
11850         });
11851
11852         return ret;
11853     },
11854
11855     /**
11856      * Clears all invalid messages in this form.
11857      * @return {BasicForm} this
11858      */
11859     clearInvalid : function(){
11860         var items = this.getItems();
11861
11862         items.each(function(f){
11863            f.clearInvalid();
11864         });
11865
11866         return this;
11867     },
11868
11869     /**
11870      * Resets this form.
11871      * @return {BasicForm} this
11872      */
11873     reset : function(){
11874         var items = this.getItems();
11875         items.each(function(f){
11876             f.reset();
11877         });
11878
11879         Roo.each(this.childForms || [], function (f) {
11880             f.reset();
11881         });
11882
11883
11884         return this;
11885     },
11886     
11887     getItems : function()
11888     {
11889         var r=new Roo.util.MixedCollection(false, function(o){
11890             return o.id || (o.id = Roo.id());
11891         });
11892         var iter = function(el) {
11893             if (el.inputEl) {
11894                 r.add(el);
11895             }
11896             if (!el.items) {
11897                 return;
11898             }
11899             Roo.each(el.items,function(e) {
11900                 iter(e);
11901             });
11902         };
11903
11904         iter(this);
11905         return r;
11906     },
11907     
11908     hideFields : function(items)
11909     {
11910         Roo.each(items, function(i){
11911             
11912             var f = this.findField(i);
11913             
11914             if(!f){
11915                 return;
11916             }
11917             
11918             f.hide();
11919             
11920         }, this);
11921     },
11922     
11923     showFields : function(items)
11924     {
11925         Roo.each(items, function(i){
11926             
11927             var f = this.findField(i);
11928             
11929             if(!f){
11930                 return;
11931             }
11932             
11933             f.show();
11934             
11935         }, this);
11936     }
11937
11938 });
11939
11940 Roo.apply(Roo.bootstrap.form.Form, {
11941     
11942     popover : {
11943         
11944         padding : 5,
11945         
11946         isApplied : false,
11947         
11948         isMasked : false,
11949         
11950         form : false,
11951         
11952         target : false,
11953         
11954         toolTip : false,
11955         
11956         intervalID : false,
11957         
11958         maskEl : false,
11959         
11960         apply : function()
11961         {
11962             if(this.isApplied){
11963                 return;
11964             }
11965             
11966             this.maskEl = {
11967                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11968                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11969                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11970                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11971             };
11972             
11973             this.maskEl.top.enableDisplayMode("block");
11974             this.maskEl.left.enableDisplayMode("block");
11975             this.maskEl.bottom.enableDisplayMode("block");
11976             this.maskEl.right.enableDisplayMode("block");
11977             
11978             this.toolTip = new Roo.bootstrap.Tooltip({
11979                 cls : 'roo-form-error-popover',
11980                 alignment : {
11981                     'left' : ['r-l', [-2,0], 'right'],
11982                     'right' : ['l-r', [2,0], 'left'],
11983                     'bottom' : ['tl-bl', [0,2], 'top'],
11984                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11985                 }
11986             });
11987             
11988             this.toolTip.render(Roo.get(document.body));
11989
11990             this.toolTip.el.enableDisplayMode("block");
11991             
11992             Roo.get(document.body).on('click', function(){
11993                 this.unmask();
11994             }, this);
11995             
11996             Roo.get(document.body).on('touchstart', function(){
11997                 this.unmask();
11998             }, this);
11999             
12000             this.isApplied = true
12001         },
12002         
12003         mask : function(form, target)
12004         {
12005             this.form = form;
12006             
12007             this.target = target;
12008             
12009             if(!this.form.errorMask || !target.el){
12010                 return;
12011             }
12012             
12013             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12014             
12015             Roo.log(scrollable);
12016             
12017             var ot = this.target.el.calcOffsetsTo(scrollable);
12018             
12019             var scrollTo = ot[1] - this.form.maskOffset;
12020             
12021             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12022             
12023             scrollable.scrollTo('top', scrollTo);
12024             
12025             var box = this.target.el.getBox();
12026             Roo.log(box);
12027             var zIndex = Roo.bootstrap.Modal.zIndex++;
12028
12029             
12030             this.maskEl.top.setStyle('position', 'absolute');
12031             this.maskEl.top.setStyle('z-index', zIndex);
12032             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12033             this.maskEl.top.setLeft(0);
12034             this.maskEl.top.setTop(0);
12035             this.maskEl.top.show();
12036             
12037             this.maskEl.left.setStyle('position', 'absolute');
12038             this.maskEl.left.setStyle('z-index', zIndex);
12039             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12040             this.maskEl.left.setLeft(0);
12041             this.maskEl.left.setTop(box.y - this.padding);
12042             this.maskEl.left.show();
12043
12044             this.maskEl.bottom.setStyle('position', 'absolute');
12045             this.maskEl.bottom.setStyle('z-index', zIndex);
12046             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12047             this.maskEl.bottom.setLeft(0);
12048             this.maskEl.bottom.setTop(box.bottom + this.padding);
12049             this.maskEl.bottom.show();
12050
12051             this.maskEl.right.setStyle('position', 'absolute');
12052             this.maskEl.right.setStyle('z-index', zIndex);
12053             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12054             this.maskEl.right.setLeft(box.right + this.padding);
12055             this.maskEl.right.setTop(box.y - this.padding);
12056             this.maskEl.right.show();
12057
12058             this.toolTip.bindEl = this.target.el;
12059
12060             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12061
12062             var tip = this.target.blankText;
12063
12064             if(this.target.getValue() !== '' ) {
12065                 
12066                 if (this.target.invalidText.length) {
12067                     tip = this.target.invalidText;
12068                 } else if (this.target.regexText.length){
12069                     tip = this.target.regexText;
12070                 }
12071             }
12072
12073             this.toolTip.show(tip);
12074
12075             this.intervalID = window.setInterval(function() {
12076                 Roo.bootstrap.form.Form.popover.unmask();
12077             }, 10000);
12078
12079             window.onwheel = function(){ return false;};
12080             
12081             (function(){ this.isMasked = true; }).defer(500, this);
12082             
12083         },
12084         
12085         unmask : function()
12086         {
12087             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12088                 return;
12089             }
12090             
12091             this.maskEl.top.setStyle('position', 'absolute');
12092             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12093             this.maskEl.top.hide();
12094
12095             this.maskEl.left.setStyle('position', 'absolute');
12096             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12097             this.maskEl.left.hide();
12098
12099             this.maskEl.bottom.setStyle('position', 'absolute');
12100             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12101             this.maskEl.bottom.hide();
12102
12103             this.maskEl.right.setStyle('position', 'absolute');
12104             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12105             this.maskEl.right.hide();
12106             
12107             this.toolTip.hide();
12108             
12109             this.toolTip.el.hide();
12110             
12111             window.onwheel = function(){ return true;};
12112             
12113             if(this.intervalID){
12114                 window.clearInterval(this.intervalID);
12115                 this.intervalID = false;
12116             }
12117             
12118             this.isMasked = false;
12119             
12120         }
12121         
12122     }
12123     
12124 });
12125
12126 /*
12127  * Based on:
12128  * Ext JS Library 1.1.1
12129  * Copyright(c) 2006-2007, Ext JS, LLC.
12130  *
12131  * Originally Released Under LGPL - original licence link has changed is not relivant.
12132  *
12133  * Fork - LGPL
12134  * <script type="text/javascript">
12135  */
12136 /**
12137  * @class Roo.form.VTypes
12138  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12139  * @static
12140  */
12141 Roo.form.VTypes = function(){
12142     // closure these in so they are only created once.
12143     var alpha = /^[a-zA-Z_]+$/;
12144     var alphanum = /^[a-zA-Z0-9_]+$/;
12145     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12146     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12147
12148     // All these messages and functions are configurable
12149     return {
12150         /**
12151          * The function used to validate email addresses
12152          * @param {String} value The email address
12153          */
12154         'email' : function(v){
12155             return email.test(v);
12156         },
12157         /**
12158          * The error text to display when the email validation function returns false
12159          * @type String
12160          */
12161         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12162         /**
12163          * The keystroke filter mask to be applied on email input
12164          * @type RegExp
12165          */
12166         'emailMask' : /[a-z0-9_\.\-@]/i,
12167
12168         /**
12169          * The function used to validate URLs
12170          * @param {String} value The URL
12171          */
12172         'url' : function(v){
12173             return url.test(v);
12174         },
12175         /**
12176          * The error text to display when the url validation function returns false
12177          * @type String
12178          */
12179         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12180         
12181         /**
12182          * The function used to validate alpha values
12183          * @param {String} value The value
12184          */
12185         'alpha' : function(v){
12186             return alpha.test(v);
12187         },
12188         /**
12189          * The error text to display when the alpha validation function returns false
12190          * @type String
12191          */
12192         'alphaText' : 'This field should only contain letters and _',
12193         /**
12194          * The keystroke filter mask to be applied on alpha input
12195          * @type RegExp
12196          */
12197         'alphaMask' : /[a-z_]/i,
12198
12199         /**
12200          * The function used to validate alphanumeric values
12201          * @param {String} value The value
12202          */
12203         'alphanum' : function(v){
12204             return alphanum.test(v);
12205         },
12206         /**
12207          * The error text to display when the alphanumeric validation function returns false
12208          * @type String
12209          */
12210         'alphanumText' : 'This field should only contain letters, numbers and _',
12211         /**
12212          * The keystroke filter mask to be applied on alphanumeric input
12213          * @type RegExp
12214          */
12215         'alphanumMask' : /[a-z0-9_]/i
12216     };
12217 }();/*
12218  * - LGPL
12219  *
12220  * Input
12221  * 
12222  */
12223
12224 /**
12225  * @class Roo.bootstrap.form.Input
12226  * @extends Roo.bootstrap.Component
12227  * Bootstrap Input class
12228  * @cfg {Boolean} disabled is it disabled
12229  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12230  * @cfg {String} name name of the input
12231  * @cfg {string} fieldLabel - the label associated
12232  * @cfg {string} placeholder - placeholder to put in text.
12233  * @cfg {string} before - input group add on before
12234  * @cfg {string} after - input group add on after
12235  * @cfg {string} size - (lg|sm) or leave empty..
12236  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12237  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12238  * @cfg {Number} md colspan out of 12 for computer-sized screens
12239  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12240  * @cfg {string} value default value of the input
12241  * @cfg {Number} labelWidth set the width of label 
12242  * @cfg {Number} labellg set the width of label (1-12)
12243  * @cfg {Number} labelmd set the width of label (1-12)
12244  * @cfg {Number} labelsm set the width of label (1-12)
12245  * @cfg {Number} labelxs set the width of label (1-12)
12246  * @cfg {String} labelAlign (top|left)
12247  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12248  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12249  * @cfg {String} indicatorpos (left|right) default left
12250  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12251  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12252  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12253  * @cfg {Roo.bootstrap.Button} before Button to show before
12254  * @cfg {Roo.bootstrap.Button} afterButton to show before
12255  * @cfg {String} align (left|center|right) Default left
12256  * @cfg {Boolean} forceFeedback (true|false) Default false
12257  * 
12258  * @constructor
12259  * Create a new Input
12260  * @param {Object} config The config object
12261  */
12262
12263 Roo.bootstrap.form.Input = function(config){
12264     
12265     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12266     
12267     this.addEvents({
12268         /**
12269          * @event focus
12270          * Fires when this field receives input focus.
12271          * @param {Roo.form.Field} this
12272          */
12273         focus : true,
12274         /**
12275          * @event blur
12276          * Fires when this field loses input focus.
12277          * @param {Roo.form.Field} this
12278          */
12279         blur : true,
12280         /**
12281          * @event specialkey
12282          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12283          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12284          * @param {Roo.form.Field} this
12285          * @param {Roo.EventObject} e The event object
12286          */
12287         specialkey : true,
12288         /**
12289          * @event change
12290          * Fires just before the field blurs if the field value has changed.
12291          * @param {Roo.form.Field} this
12292          * @param {Mixed} newValue The new value
12293          * @param {Mixed} oldValue The original value
12294          */
12295         change : true,
12296         /**
12297          * @event invalid
12298          * Fires after the field has been marked as invalid.
12299          * @param {Roo.form.Field} this
12300          * @param {String} msg The validation message
12301          */
12302         invalid : true,
12303         /**
12304          * @event valid
12305          * Fires after the field has been validated with no errors.
12306          * @param {Roo.form.Field} this
12307          */
12308         valid : true,
12309          /**
12310          * @event keyup
12311          * Fires after the key up
12312          * @param {Roo.form.Field} this
12313          * @param {Roo.EventObject}  e The event Object
12314          */
12315         keyup : true,
12316         /**
12317          * @event paste
12318          * Fires after the user pastes into input
12319          * @param {Roo.form.Field} this
12320          * @param {Roo.EventObject}  e The event Object
12321          */
12322         paste : true
12323     });
12324 };
12325
12326 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12327      /**
12328      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12329       automatic validation (defaults to "keyup").
12330      */
12331     validationEvent : "keyup",
12332      /**
12333      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12334      */
12335     validateOnBlur : true,
12336     /**
12337      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12338      */
12339     validationDelay : 250,
12340      /**
12341      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12342      */
12343     focusClass : "x-form-focus",  // not needed???
12344     
12345        
12346     /**
12347      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12348      */
12349     invalidClass : "has-warning",
12350     
12351     /**
12352      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12353      */
12354     validClass : "has-success",
12355     
12356     /**
12357      * @cfg {Boolean} hasFeedback (true|false) default true
12358      */
12359     hasFeedback : true,
12360     
12361     /**
12362      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12363      */
12364     invalidFeedbackClass : "glyphicon-warning-sign",
12365     
12366     /**
12367      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12368      */
12369     validFeedbackClass : "glyphicon-ok",
12370     
12371     /**
12372      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12373      */
12374     selectOnFocus : false,
12375     
12376      /**
12377      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12378      */
12379     maskRe : null,
12380        /**
12381      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12382      */
12383     vtype : null,
12384     
12385       /**
12386      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12387      */
12388     disableKeyFilter : false,
12389     
12390        /**
12391      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12392      */
12393     disabled : false,
12394      /**
12395      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12396      */
12397     allowBlank : true,
12398     /**
12399      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12400      */
12401     blankText : "Please complete this mandatory field",
12402     
12403      /**
12404      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12405      */
12406     minLength : 0,
12407     /**
12408      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12409      */
12410     maxLength : Number.MAX_VALUE,
12411     /**
12412      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12413      */
12414     minLengthText : "The minimum length for this field is {0}",
12415     /**
12416      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12417      */
12418     maxLengthText : "The maximum length for this field is {0}",
12419   
12420     
12421     /**
12422      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12423      * If available, this function will be called only after the basic validators all return true, and will be passed the
12424      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12425      */
12426     validator : null,
12427     /**
12428      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12429      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12430      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12431      */
12432     regex : null,
12433     /**
12434      * @cfg {String} regexText -- Depricated - use Invalid Text
12435      */
12436     regexText : "",
12437     
12438     /**
12439      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12440      */
12441     invalidText : "",
12442     
12443     
12444     
12445     autocomplete: false,
12446     
12447     
12448     fieldLabel : '',
12449     inputType : 'text',
12450     
12451     name : false,
12452     placeholder: false,
12453     before : false,
12454     after : false,
12455     size : false,
12456     hasFocus : false,
12457     preventMark: false,
12458     isFormField : true,
12459     value : '',
12460     labelWidth : 2,
12461     labelAlign : false,
12462     readOnly : false,
12463     align : false,
12464     formatedValue : false,
12465     forceFeedback : false,
12466     
12467     indicatorpos : 'left',
12468     
12469     labellg : 0,
12470     labelmd : 0,
12471     labelsm : 0,
12472     labelxs : 0,
12473     
12474     capture : '',
12475     accept : '',
12476     
12477     parentLabelAlign : function()
12478     {
12479         var parent = this;
12480         while (parent.parent()) {
12481             parent = parent.parent();
12482             if (typeof(parent.labelAlign) !='undefined') {
12483                 return parent.labelAlign;
12484             }
12485         }
12486         return 'left';
12487         
12488     },
12489     
12490     getAutoCreate : function()
12491     {
12492         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12493         
12494         var id = Roo.id();
12495         
12496         var cfg = {};
12497         
12498         if(this.inputType != 'hidden'){
12499             cfg.cls = 'form-group' //input-group
12500         }
12501         
12502         var input =  {
12503             tag: 'input',
12504             id : id,
12505             type : this.inputType,
12506             value : this.value,
12507             cls : 'form-control',
12508             placeholder : this.placeholder || '',
12509             autocomplete : this.autocomplete || 'new-password'
12510         };
12511         if (this.inputType == 'file') {
12512             input.style = 'overflow:hidden'; // why not in CSS?
12513         }
12514         
12515         if(this.capture.length){
12516             input.capture = this.capture;
12517         }
12518         
12519         if(this.accept.length){
12520             input.accept = this.accept + "/*";
12521         }
12522         
12523         if(this.align){
12524             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12525         }
12526         
12527         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12528             input.maxLength = this.maxLength;
12529         }
12530         
12531         if (this.disabled) {
12532             input.disabled=true;
12533         }
12534         
12535         if (this.readOnly) {
12536             input.readonly=true;
12537         }
12538         
12539         if (this.name) {
12540             input.name = this.name;
12541         }
12542         
12543         if (this.size) {
12544             input.cls += ' input-' + this.size;
12545         }
12546         
12547         var settings=this;
12548         ['xs','sm','md','lg'].map(function(size){
12549             if (settings[size]) {
12550                 cfg.cls += ' col-' + size + '-' + settings[size];
12551             }
12552         });
12553         
12554         var inputblock = input;
12555         
12556         var feedback = {
12557             tag: 'span',
12558             cls: 'glyphicon form-control-feedback'
12559         };
12560             
12561         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12562             
12563             inputblock = {
12564                 cls : 'has-feedback',
12565                 cn :  [
12566                     input,
12567                     feedback
12568                 ] 
12569             };  
12570         }
12571         
12572         if (this.before || this.after) {
12573             
12574             inputblock = {
12575                 cls : 'input-group',
12576                 cn :  [] 
12577             };
12578             
12579             if (this.before && typeof(this.before) == 'string') {
12580                 
12581                 inputblock.cn.push({
12582                     tag :'span',
12583                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12584                     html : this.before
12585                 });
12586             }
12587             if (this.before && typeof(this.before) == 'object') {
12588                 this.before = Roo.factory(this.before);
12589                 
12590                 inputblock.cn.push({
12591                     tag :'span',
12592                     cls : 'roo-input-before input-group-prepend   input-group-' +
12593                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12594                 });
12595             }
12596             
12597             inputblock.cn.push(input);
12598             
12599             if (this.after && typeof(this.after) == 'string') {
12600                 inputblock.cn.push({
12601                     tag :'span',
12602                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12603                     html : this.after
12604                 });
12605             }
12606             if (this.after && typeof(this.after) == 'object') {
12607                 this.after = Roo.factory(this.after);
12608                 
12609                 inputblock.cn.push({
12610                     tag :'span',
12611                     cls : 'roo-input-after input-group-append  input-group-' +
12612                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12613                 });
12614             }
12615             
12616             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12617                 inputblock.cls += ' has-feedback';
12618                 inputblock.cn.push(feedback);
12619             }
12620         };
12621         var indicator = {
12622             tag : 'i',
12623             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12624             tooltip : 'This field is required'
12625         };
12626         if (this.allowBlank ) {
12627             indicator.style = this.allowBlank ? ' display:none' : '';
12628         }
12629         if (align ==='left' && this.fieldLabel.length) {
12630             
12631             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12632             
12633             cfg.cn = [
12634                 indicator,
12635                 {
12636                     tag: 'label',
12637                     'for' :  id,
12638                     cls : 'control-label col-form-label',
12639                     html : this.fieldLabel
12640
12641                 },
12642                 {
12643                     cls : "", 
12644                     cn: [
12645                         inputblock
12646                     ]
12647                 }
12648             ];
12649             
12650             var labelCfg = cfg.cn[1];
12651             var contentCfg = cfg.cn[2];
12652             
12653             if(this.indicatorpos == 'right'){
12654                 cfg.cn = [
12655                     {
12656                         tag: 'label',
12657                         'for' :  id,
12658                         cls : 'control-label col-form-label',
12659                         cn : [
12660                             {
12661                                 tag : 'span',
12662                                 html : this.fieldLabel
12663                             },
12664                             indicator
12665                         ]
12666                     },
12667                     {
12668                         cls : "",
12669                         cn: [
12670                             inputblock
12671                         ]
12672                     }
12673
12674                 ];
12675                 
12676                 labelCfg = cfg.cn[0];
12677                 contentCfg = cfg.cn[1];
12678             
12679             }
12680             
12681             if(this.labelWidth > 12){
12682                 labelCfg.style = "width: " + this.labelWidth + 'px';
12683             }
12684             
12685             if(this.labelWidth < 13 && this.labelmd == 0){
12686                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12687             }
12688             
12689             if(this.labellg > 0){
12690                 labelCfg.cls += ' col-lg-' + this.labellg;
12691                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12692             }
12693             
12694             if(this.labelmd > 0){
12695                 labelCfg.cls += ' col-md-' + this.labelmd;
12696                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12697             }
12698             
12699             if(this.labelsm > 0){
12700                 labelCfg.cls += ' col-sm-' + this.labelsm;
12701                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12702             }
12703             
12704             if(this.labelxs > 0){
12705                 labelCfg.cls += ' col-xs-' + this.labelxs;
12706                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12707             }
12708             
12709             
12710         } else if ( this.fieldLabel.length) {
12711                 
12712             
12713             
12714             cfg.cn = [
12715                 {
12716                     tag : 'i',
12717                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12718                     tooltip : 'This field is required',
12719                     style : this.allowBlank ? ' display:none' : '' 
12720                 },
12721                 {
12722                     tag: 'label',
12723                    //cls : 'input-group-addon',
12724                     html : this.fieldLabel
12725
12726                 },
12727
12728                inputblock
12729
12730            ];
12731            
12732            if(this.indicatorpos == 'right'){
12733        
12734                 cfg.cn = [
12735                     {
12736                         tag: 'label',
12737                        //cls : 'input-group-addon',
12738                         html : this.fieldLabel
12739
12740                     },
12741                     {
12742                         tag : 'i',
12743                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12744                         tooltip : 'This field is required',
12745                         style : this.allowBlank ? ' display:none' : '' 
12746                     },
12747
12748                    inputblock
12749
12750                ];
12751
12752             }
12753
12754         } else {
12755             
12756             cfg.cn = [
12757
12758                     inputblock
12759
12760             ];
12761                 
12762                 
12763         };
12764         
12765         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12766            cfg.cls += ' navbar-form';
12767         }
12768         
12769         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12770             // on BS4 we do this only if not form 
12771             cfg.cls += ' navbar-form';
12772             cfg.tag = 'li';
12773         }
12774         
12775         return cfg;
12776         
12777     },
12778     /**
12779      * return the real input element.
12780      */
12781     inputEl: function ()
12782     {
12783         return this.el.select('input.form-control',true).first();
12784     },
12785     
12786     tooltipEl : function()
12787     {
12788         return this.inputEl();
12789     },
12790     
12791     indicatorEl : function()
12792     {
12793         if (Roo.bootstrap.version == 4) {
12794             return false; // not enabled in v4 yet.
12795         }
12796         
12797         var indicator = this.el.select('i.roo-required-indicator',true).first();
12798         
12799         if(!indicator){
12800             return false;
12801         }
12802         
12803         return indicator;
12804         
12805     },
12806     
12807     setDisabled : function(v)
12808     {
12809         var i  = this.inputEl().dom;
12810         if (!v) {
12811             i.removeAttribute('disabled');
12812             return;
12813             
12814         }
12815         i.setAttribute('disabled','true');
12816     },
12817     initEvents : function()
12818     {
12819           
12820         this.inputEl().on("keydown" , this.fireKey,  this);
12821         this.inputEl().on("focus", this.onFocus,  this);
12822         this.inputEl().on("blur", this.onBlur,  this);
12823         
12824         this.inputEl().relayEvent('keyup', this);
12825         this.inputEl().relayEvent('paste', this);
12826         
12827         this.indicator = this.indicatorEl();
12828         
12829         if(this.indicator){
12830             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12831         }
12832  
12833         // reference to original value for reset
12834         this.originalValue = this.getValue();
12835         //Roo.form.TextField.superclass.initEvents.call(this);
12836         if(this.validationEvent == 'keyup'){
12837             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12838             this.inputEl().on('keyup', this.filterValidation, this);
12839         }
12840         else if(this.validationEvent !== false){
12841             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12842         }
12843         
12844         if(this.selectOnFocus){
12845             this.on("focus", this.preFocus, this);
12846             
12847         }
12848         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12849             this.inputEl().on("keypress", this.filterKeys, this);
12850         } else {
12851             this.inputEl().relayEvent('keypress', this);
12852         }
12853        /* if(this.grow){
12854             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12855             this.el.on("click", this.autoSize,  this);
12856         }
12857         */
12858         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12859             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12860         }
12861         
12862         if (typeof(this.before) == 'object') {
12863             this.before.render(this.el.select('.roo-input-before',true).first());
12864         }
12865         if (typeof(this.after) == 'object') {
12866             this.after.render(this.el.select('.roo-input-after',true).first());
12867         }
12868         
12869         this.inputEl().on('change', this.onChange, this);
12870         
12871     },
12872     filterValidation : function(e){
12873         if(!e.isNavKeyPress()){
12874             this.validationTask.delay(this.validationDelay);
12875         }
12876     },
12877      /**
12878      * Validates the field value
12879      * @return {Boolean} True if the value is valid, else false
12880      */
12881     validate : function(){
12882         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12883         if(this.disabled || this.validateValue(this.getRawValue())){
12884             this.markValid();
12885             return true;
12886         }
12887         
12888         this.markInvalid();
12889         return false;
12890     },
12891     
12892     
12893     /**
12894      * Validates a value according to the field's validation rules and marks the field as invalid
12895      * if the validation fails
12896      * @param {Mixed} value The value to validate
12897      * @return {Boolean} True if the value is valid, else false
12898      */
12899     validateValue : function(value)
12900     {
12901         if(this.getVisibilityEl().hasClass('hidden')){
12902             return true;
12903         }
12904         
12905         if(value.length < 1)  { // if it's blank
12906             if(this.allowBlank){
12907                 return true;
12908             }
12909             return false;
12910         }
12911         
12912         if(value.length < this.minLength){
12913             return false;
12914         }
12915         if(value.length > this.maxLength){
12916             return false;
12917         }
12918         if(this.vtype){
12919             var vt = Roo.form.VTypes;
12920             if(!vt[this.vtype](value, this)){
12921                 return false;
12922             }
12923         }
12924         if(typeof this.validator == "function"){
12925             var msg = this.validator(value);
12926             if(msg !== true){
12927                 return false;
12928             }
12929             if (typeof(msg) == 'string') {
12930                 this.invalidText = msg;
12931             }
12932         }
12933         
12934         if(this.regex && !this.regex.test(value)){
12935             return false;
12936         }
12937         
12938         return true;
12939     },
12940     
12941      // private
12942     fireKey : function(e){
12943         //Roo.log('field ' + e.getKey());
12944         if(e.isNavKeyPress()){
12945             this.fireEvent("specialkey", this, e);
12946         }
12947     },
12948     focus : function (selectText){
12949         if(this.rendered){
12950             this.inputEl().focus();
12951             if(selectText === true){
12952                 this.inputEl().dom.select();
12953             }
12954         }
12955         return this;
12956     } ,
12957     
12958     onFocus : function(){
12959         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12960            // this.el.addClass(this.focusClass);
12961         }
12962         if(!this.hasFocus){
12963             this.hasFocus = true;
12964             this.startValue = this.getValue();
12965             this.fireEvent("focus", this);
12966         }
12967     },
12968     
12969     beforeBlur : Roo.emptyFn,
12970
12971     
12972     // private
12973     onBlur : function(){
12974         this.beforeBlur();
12975         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12976             //this.el.removeClass(this.focusClass);
12977         }
12978         this.hasFocus = false;
12979         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12980             this.validate();
12981         }
12982         var v = this.getValue();
12983         if(String(v) !== String(this.startValue)){
12984             this.fireEvent('change', this, v, this.startValue);
12985         }
12986         this.fireEvent("blur", this);
12987     },
12988     
12989     onChange : function(e)
12990     {
12991         var v = this.getValue();
12992         if(String(v) !== String(this.startValue)){
12993             this.fireEvent('change', this, v, this.startValue);
12994         }
12995         
12996     },
12997     
12998     /**
12999      * Resets the current field value to the originally loaded value and clears any validation messages
13000      */
13001     reset : function(){
13002         this.setValue(this.originalValue);
13003         this.validate();
13004     },
13005      /**
13006      * Returns the name of the field
13007      * @return {Mixed} name The name field
13008      */
13009     getName: function(){
13010         return this.name;
13011     },
13012      /**
13013      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13014      * @return {Mixed} value The field value
13015      */
13016     getValue : function(){
13017         
13018         var v = this.inputEl().getValue();
13019         
13020         return v;
13021     },
13022     /**
13023      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13024      * @return {Mixed} value The field value
13025      */
13026     getRawValue : function(){
13027         var v = this.inputEl().getValue();
13028         
13029         return v;
13030     },
13031     
13032     /**
13033      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13034      * @param {Mixed} value The value to set
13035      */
13036     setRawValue : function(v){
13037         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13038     },
13039     
13040     selectText : function(start, end){
13041         var v = this.getRawValue();
13042         if(v.length > 0){
13043             start = start === undefined ? 0 : start;
13044             end = end === undefined ? v.length : end;
13045             var d = this.inputEl().dom;
13046             if(d.setSelectionRange){
13047                 d.setSelectionRange(start, end);
13048             }else if(d.createTextRange){
13049                 var range = d.createTextRange();
13050                 range.moveStart("character", start);
13051                 range.moveEnd("character", v.length-end);
13052                 range.select();
13053             }
13054         }
13055     },
13056     
13057     /**
13058      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13059      * @param {Mixed} value The value to set
13060      */
13061     setValue : function(v){
13062         this.value = v;
13063         if(this.rendered){
13064             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13065             this.validate();
13066         }
13067     },
13068     
13069     /*
13070     processValue : function(value){
13071         if(this.stripCharsRe){
13072             var newValue = value.replace(this.stripCharsRe, '');
13073             if(newValue !== value){
13074                 this.setRawValue(newValue);
13075                 return newValue;
13076             }
13077         }
13078         return value;
13079     },
13080   */
13081     preFocus : function(){
13082         
13083         if(this.selectOnFocus){
13084             this.inputEl().dom.select();
13085         }
13086     },
13087     filterKeys : function(e){
13088         var k = e.getKey();
13089         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13090             return;
13091         }
13092         var c = e.getCharCode(), cc = String.fromCharCode(c);
13093         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13094             return;
13095         }
13096         if(!this.maskRe.test(cc)){
13097             e.stopEvent();
13098         }
13099     },
13100      /**
13101      * Clear any invalid styles/messages for this field
13102      */
13103     clearInvalid : function(){
13104         
13105         if(!this.el || this.preventMark){ // not rendered
13106             return;
13107         }
13108         
13109         
13110         this.el.removeClass([this.invalidClass, 'is-invalid']);
13111         
13112         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13113             
13114             var feedback = this.el.select('.form-control-feedback', true).first();
13115             
13116             if(feedback){
13117                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13118             }
13119             
13120         }
13121         
13122         if(this.indicator){
13123             this.indicator.removeClass('visible');
13124             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13125         }
13126         
13127         this.fireEvent('valid', this);
13128     },
13129     
13130      /**
13131      * Mark this field as valid
13132      */
13133     markValid : function()
13134     {
13135         if(!this.el  || this.preventMark){ // not rendered...
13136             return;
13137         }
13138         
13139         this.el.removeClass([this.invalidClass, this.validClass]);
13140         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13141
13142         var feedback = this.el.select('.form-control-feedback', true).first();
13143             
13144         if(feedback){
13145             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13146         }
13147         
13148         if(this.indicator){
13149             this.indicator.removeClass('visible');
13150             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13151         }
13152         
13153         if(this.disabled){
13154             return;
13155         }
13156         
13157            
13158         if(this.allowBlank && !this.getRawValue().length){
13159             return;
13160         }
13161         if (Roo.bootstrap.version == 3) {
13162             this.el.addClass(this.validClass);
13163         } else {
13164             this.inputEl().addClass('is-valid');
13165         }
13166
13167         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13168             
13169             var feedback = this.el.select('.form-control-feedback', true).first();
13170             
13171             if(feedback){
13172                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13173                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13174             }
13175             
13176         }
13177         
13178         this.fireEvent('valid', this);
13179     },
13180     
13181      /**
13182      * Mark this field as invalid
13183      * @param {String} msg The validation message
13184      */
13185     markInvalid : function(msg)
13186     {
13187         if(!this.el  || this.preventMark){ // not rendered
13188             return;
13189         }
13190         
13191         this.el.removeClass([this.invalidClass, this.validClass]);
13192         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13193         
13194         var feedback = this.el.select('.form-control-feedback', true).first();
13195             
13196         if(feedback){
13197             this.el.select('.form-control-feedback', true).first().removeClass(
13198                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13199         }
13200
13201         if(this.disabled){
13202             return;
13203         }
13204         
13205         if(this.allowBlank && !this.getRawValue().length){
13206             return;
13207         }
13208         
13209         if(this.indicator){
13210             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13211             this.indicator.addClass('visible');
13212         }
13213         if (Roo.bootstrap.version == 3) {
13214             this.el.addClass(this.invalidClass);
13215         } else {
13216             this.inputEl().addClass('is-invalid');
13217         }
13218         
13219         
13220         
13221         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13222             
13223             var feedback = this.el.select('.form-control-feedback', true).first();
13224             
13225             if(feedback){
13226                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13227                 
13228                 if(this.getValue().length || this.forceFeedback){
13229                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13230                 }
13231                 
13232             }
13233             
13234         }
13235         
13236         this.fireEvent('invalid', this, msg);
13237     },
13238     // private
13239     SafariOnKeyDown : function(event)
13240     {
13241         // this is a workaround for a password hang bug on chrome/ webkit.
13242         if (this.inputEl().dom.type != 'password') {
13243             return;
13244         }
13245         
13246         var isSelectAll = false;
13247         
13248         if(this.inputEl().dom.selectionEnd > 0){
13249             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13250         }
13251         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13252             event.preventDefault();
13253             this.setValue('');
13254             return;
13255         }
13256         
13257         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13258             
13259             event.preventDefault();
13260             // this is very hacky as keydown always get's upper case.
13261             //
13262             var cc = String.fromCharCode(event.getCharCode());
13263             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13264             
13265         }
13266     },
13267     adjustWidth : function(tag, w){
13268         tag = tag.toLowerCase();
13269         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13270             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13271                 if(tag == 'input'){
13272                     return w + 2;
13273                 }
13274                 if(tag == 'textarea'){
13275                     return w-2;
13276                 }
13277             }else if(Roo.isOpera){
13278                 if(tag == 'input'){
13279                     return w + 2;
13280                 }
13281                 if(tag == 'textarea'){
13282                     return w-2;
13283                 }
13284             }
13285         }
13286         return w;
13287     },
13288     
13289     setFieldLabel : function(v)
13290     {
13291         if(!this.rendered){
13292             return;
13293         }
13294         
13295         if(this.indicatorEl()){
13296             var ar = this.el.select('label > span',true);
13297             
13298             if (ar.elements.length) {
13299                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13300                 this.fieldLabel = v;
13301                 return;
13302             }
13303             
13304             var br = this.el.select('label',true);
13305             
13306             if(br.elements.length) {
13307                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13308                 this.fieldLabel = v;
13309                 return;
13310             }
13311             
13312             Roo.log('Cannot Found any of label > span || label in input');
13313             return;
13314         }
13315         
13316         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13317         this.fieldLabel = v;
13318         
13319         
13320     }
13321 });
13322
13323  
13324 /*
13325  * - LGPL
13326  *
13327  * Input
13328  * 
13329  */
13330
13331 /**
13332  * @class Roo.bootstrap.form.TextArea
13333  * @extends Roo.bootstrap.form.Input
13334  * Bootstrap TextArea class
13335  * @cfg {Number} cols Specifies the visible width of a text area
13336  * @cfg {Number} rows Specifies the visible number of lines in a text area
13337  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13338  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13339  * @cfg {string} html text
13340  * 
13341  * @constructor
13342  * Create a new TextArea
13343  * @param {Object} config The config object
13344  */
13345
13346 Roo.bootstrap.form.TextArea = function(config){
13347     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13348    
13349 };
13350
13351 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13352      
13353     cols : false,
13354     rows : 5,
13355     readOnly : false,
13356     warp : 'soft',
13357     resize : false,
13358     value: false,
13359     html: false,
13360     
13361     getAutoCreate : function(){
13362         
13363         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13364         
13365         var id = Roo.id();
13366         
13367         var cfg = {};
13368         
13369         if(this.inputType != 'hidden'){
13370             cfg.cls = 'form-group' //input-group
13371         }
13372         
13373         var input =  {
13374             tag: 'textarea',
13375             id : id,
13376             warp : this.warp,
13377             rows : this.rows,
13378             value : this.value || '',
13379             html: this.html || '',
13380             cls : 'form-control',
13381             placeholder : this.placeholder || '' 
13382             
13383         };
13384         
13385         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13386             input.maxLength = this.maxLength;
13387         }
13388         
13389         if(this.resize){
13390             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13391         }
13392         
13393         if(this.cols){
13394             input.cols = this.cols;
13395         }
13396         
13397         if (this.readOnly) {
13398             input.readonly = true;
13399         }
13400         
13401         if (this.name) {
13402             input.name = this.name;
13403         }
13404         
13405         if (this.size) {
13406             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13407         }
13408         
13409         var settings=this;
13410         ['xs','sm','md','lg'].map(function(size){
13411             if (settings[size]) {
13412                 cfg.cls += ' col-' + size + '-' + settings[size];
13413             }
13414         });
13415         
13416         var inputblock = input;
13417         
13418         if(this.hasFeedback && !this.allowBlank){
13419             
13420             var feedback = {
13421                 tag: 'span',
13422                 cls: 'glyphicon form-control-feedback'
13423             };
13424
13425             inputblock = {
13426                 cls : 'has-feedback',
13427                 cn :  [
13428                     input,
13429                     feedback
13430                 ] 
13431             };  
13432         }
13433         
13434         
13435         if (this.before || this.after) {
13436             
13437             inputblock = {
13438                 cls : 'input-group',
13439                 cn :  [] 
13440             };
13441             if (this.before) {
13442                 inputblock.cn.push({
13443                     tag :'span',
13444                     cls : 'input-group-addon',
13445                     html : this.before
13446                 });
13447             }
13448             
13449             inputblock.cn.push(input);
13450             
13451             if(this.hasFeedback && !this.allowBlank){
13452                 inputblock.cls += ' has-feedback';
13453                 inputblock.cn.push(feedback);
13454             }
13455             
13456             if (this.after) {
13457                 inputblock.cn.push({
13458                     tag :'span',
13459                     cls : 'input-group-addon',
13460                     html : this.after
13461                 });
13462             }
13463             
13464         }
13465         
13466         if (align ==='left' && this.fieldLabel.length) {
13467             cfg.cn = [
13468                 {
13469                     tag: 'label',
13470                     'for' :  id,
13471                     cls : 'control-label',
13472                     html : this.fieldLabel
13473                 },
13474                 {
13475                     cls : "",
13476                     cn: [
13477                         inputblock
13478                     ]
13479                 }
13480
13481             ];
13482             
13483             if(this.labelWidth > 12){
13484                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13485             }
13486
13487             if(this.labelWidth < 13 && this.labelmd == 0){
13488                 this.labelmd = this.labelWidth;
13489             }
13490
13491             if(this.labellg > 0){
13492                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13493                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13494             }
13495
13496             if(this.labelmd > 0){
13497                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13498                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13499             }
13500
13501             if(this.labelsm > 0){
13502                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13503                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13504             }
13505
13506             if(this.labelxs > 0){
13507                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13508                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13509             }
13510             
13511         } else if ( this.fieldLabel.length) {
13512             cfg.cn = [
13513
13514                {
13515                    tag: 'label',
13516                    //cls : 'input-group-addon',
13517                    html : this.fieldLabel
13518
13519                },
13520
13521                inputblock
13522
13523            ];
13524
13525         } else {
13526
13527             cfg.cn = [
13528
13529                 inputblock
13530
13531             ];
13532                 
13533         }
13534         
13535         if (this.disabled) {
13536             input.disabled=true;
13537         }
13538         
13539         return cfg;
13540         
13541     },
13542     /**
13543      * return the real textarea element.
13544      */
13545     inputEl: function ()
13546     {
13547         return this.el.select('textarea.form-control',true).first();
13548     },
13549     
13550     /**
13551      * Clear any invalid styles/messages for this field
13552      */
13553     clearInvalid : function()
13554     {
13555         
13556         if(!this.el || this.preventMark){ // not rendered
13557             return;
13558         }
13559         
13560         var label = this.el.select('label', true).first();
13561         var icon = this.el.select('i.fa-star', true).first();
13562         
13563         if(label && icon){
13564             icon.remove();
13565         }
13566         this.el.removeClass( this.validClass);
13567         this.inputEl().removeClass('is-invalid');
13568          
13569         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13570             
13571             var feedback = this.el.select('.form-control-feedback', true).first();
13572             
13573             if(feedback){
13574                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13575             }
13576             
13577         }
13578         
13579         this.fireEvent('valid', this);
13580     },
13581     
13582      /**
13583      * Mark this field as valid
13584      */
13585     markValid : function()
13586     {
13587         if(!this.el  || this.preventMark){ // not rendered
13588             return;
13589         }
13590         
13591         this.el.removeClass([this.invalidClass, this.validClass]);
13592         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13593         
13594         var feedback = this.el.select('.form-control-feedback', true).first();
13595             
13596         if(feedback){
13597             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13598         }
13599
13600         if(this.disabled || this.allowBlank){
13601             return;
13602         }
13603         
13604         var label = this.el.select('label', true).first();
13605         var icon = this.el.select('i.fa-star', true).first();
13606         
13607         if(label && icon){
13608             icon.remove();
13609         }
13610         if (Roo.bootstrap.version == 3) {
13611             this.el.addClass(this.validClass);
13612         } else {
13613             this.inputEl().addClass('is-valid');
13614         }
13615         
13616         
13617         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13618             
13619             var feedback = this.el.select('.form-control-feedback', true).first();
13620             
13621             if(feedback){
13622                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13623                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13624             }
13625             
13626         }
13627         
13628         this.fireEvent('valid', this);
13629     },
13630     
13631      /**
13632      * Mark this field as invalid
13633      * @param {String} msg The validation message
13634      */
13635     markInvalid : function(msg)
13636     {
13637         if(!this.el  || this.preventMark){ // not rendered
13638             return;
13639         }
13640         
13641         this.el.removeClass([this.invalidClass, this.validClass]);
13642         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13643         
13644         var feedback = this.el.select('.form-control-feedback', true).first();
13645             
13646         if(feedback){
13647             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13648         }
13649
13650         if(this.disabled || this.allowBlank){
13651             return;
13652         }
13653         
13654         var label = this.el.select('label', true).first();
13655         var icon = this.el.select('i.fa-star', true).first();
13656         
13657         if(!this.getValue().length && label && !icon){
13658             this.el.createChild({
13659                 tag : 'i',
13660                 cls : 'text-danger fa fa-lg fa-star',
13661                 tooltip : 'This field is required',
13662                 style : 'margin-right:5px;'
13663             }, label, true);
13664         }
13665         
13666         if (Roo.bootstrap.version == 3) {
13667             this.el.addClass(this.invalidClass);
13668         } else {
13669             this.inputEl().addClass('is-invalid');
13670         }
13671         
13672         // fixme ... this may be depricated need to test..
13673         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13674             
13675             var feedback = this.el.select('.form-control-feedback', true).first();
13676             
13677             if(feedback){
13678                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13679                 
13680                 if(this.getValue().length || this.forceFeedback){
13681                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13682                 }
13683                 
13684             }
13685             
13686         }
13687         
13688         this.fireEvent('invalid', this, msg);
13689     }
13690 });
13691
13692  
13693 /*
13694  * - LGPL
13695  *
13696  * trigger field - base class for combo..
13697  * 
13698  */
13699  
13700 /**
13701  * @class Roo.bootstrap.form.TriggerField
13702  * @extends Roo.bootstrap.form.Input
13703  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13704  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13705  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13706  * for which you can provide a custom implementation.  For example:
13707  * <pre><code>
13708 var trigger = new Roo.bootstrap.form.TriggerField();
13709 trigger.onTriggerClick = myTriggerFn;
13710 trigger.applyTo('my-field');
13711 </code></pre>
13712  *
13713  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13714  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13715  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13716  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13717  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13718
13719  * @constructor
13720  * Create a new TriggerField.
13721  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13722  * to the base TextField)
13723  */
13724 Roo.bootstrap.form.TriggerField = function(config){
13725     this.mimicing = false;
13726     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13727 };
13728
13729 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13730     /**
13731      * @cfg {String} triggerClass A CSS class to apply to the trigger
13732      */
13733      /**
13734      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13735      */
13736     hideTrigger:false,
13737
13738     /**
13739      * @cfg {Boolean} removable (true|false) special filter default false
13740      */
13741     removable : false,
13742     
13743     /** @cfg {Boolean} grow @hide */
13744     /** @cfg {Number} growMin @hide */
13745     /** @cfg {Number} growMax @hide */
13746
13747     /**
13748      * @hide 
13749      * @method
13750      */
13751     autoSize: Roo.emptyFn,
13752     // private
13753     monitorTab : true,
13754     // private
13755     deferHeight : true,
13756
13757     
13758     actionMode : 'wrap',
13759     
13760     caret : false,
13761     
13762     
13763     getAutoCreate : function(){
13764        
13765         var align = this.labelAlign || this.parentLabelAlign();
13766         
13767         var id = Roo.id();
13768         
13769         var cfg = {
13770             cls: 'form-group' //input-group
13771         };
13772         
13773         
13774         var input =  {
13775             tag: 'input',
13776             id : id,
13777             type : this.inputType,
13778             cls : 'form-control',
13779             autocomplete: 'new-password',
13780             placeholder : this.placeholder || '' 
13781             
13782         };
13783         if (this.name) {
13784             input.name = this.name;
13785         }
13786         if (this.size) {
13787             input.cls += ' input-' + this.size;
13788         }
13789         
13790         if (this.disabled) {
13791             input.disabled=true;
13792         }
13793         
13794         var inputblock = input;
13795         
13796         if(this.hasFeedback && !this.allowBlank){
13797             
13798             var feedback = {
13799                 tag: 'span',
13800                 cls: 'glyphicon form-control-feedback'
13801             };
13802             
13803             if(this.removable && !this.editable  ){
13804                 inputblock = {
13805                     cls : 'has-feedback',
13806                     cn :  [
13807                         inputblock,
13808                         {
13809                             tag: 'button',
13810                             html : 'x',
13811                             cls : 'roo-combo-removable-btn close'
13812                         },
13813                         feedback
13814                     ] 
13815                 };
13816             } else {
13817                 inputblock = {
13818                     cls : 'has-feedback',
13819                     cn :  [
13820                         inputblock,
13821                         feedback
13822                     ] 
13823                 };
13824             }
13825
13826         } else {
13827             if(this.removable && !this.editable ){
13828                 inputblock = {
13829                     cls : 'roo-removable',
13830                     cn :  [
13831                         inputblock,
13832                         {
13833                             tag: 'button',
13834                             html : 'x',
13835                             cls : 'roo-combo-removable-btn close'
13836                         }
13837                     ] 
13838                 };
13839             }
13840         }
13841         
13842         if (this.before || this.after) {
13843             
13844             inputblock = {
13845                 cls : 'input-group',
13846                 cn :  [] 
13847             };
13848             if (this.before) {
13849                 inputblock.cn.push({
13850                     tag :'span',
13851                     cls : 'input-group-addon input-group-prepend input-group-text',
13852                     html : this.before
13853                 });
13854             }
13855             
13856             inputblock.cn.push(input);
13857             
13858             if(this.hasFeedback && !this.allowBlank){
13859                 inputblock.cls += ' has-feedback';
13860                 inputblock.cn.push(feedback);
13861             }
13862             
13863             if (this.after) {
13864                 inputblock.cn.push({
13865                     tag :'span',
13866                     cls : 'input-group-addon input-group-append input-group-text',
13867                     html : this.after
13868                 });
13869             }
13870             
13871         };
13872         
13873       
13874         
13875         var ibwrap = inputblock;
13876         
13877         if(this.multiple){
13878             ibwrap = {
13879                 tag: 'ul',
13880                 cls: 'roo-select2-choices',
13881                 cn:[
13882                     {
13883                         tag: 'li',
13884                         cls: 'roo-select2-search-field',
13885                         cn: [
13886
13887                             inputblock
13888                         ]
13889                     }
13890                 ]
13891             };
13892                 
13893         }
13894         
13895         var combobox = {
13896             cls: 'roo-select2-container input-group',
13897             cn: [
13898                  {
13899                     tag: 'input',
13900                     type : 'hidden',
13901                     cls: 'form-hidden-field'
13902                 },
13903                 ibwrap
13904             ]
13905         };
13906         
13907         if(!this.multiple && this.showToggleBtn){
13908             
13909             var caret = {
13910                         tag: 'span',
13911                         cls: 'caret'
13912              };
13913             if (this.caret != false) {
13914                 caret = {
13915                      tag: 'i',
13916                      cls: 'fa fa-' + this.caret
13917                 };
13918                 
13919             }
13920             
13921             combobox.cn.push({
13922                 tag :'span',
13923                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13924                 cn : [
13925                     Roo.bootstrap.version == 3 ? caret : '',
13926                     {
13927                         tag: 'span',
13928                         cls: 'combobox-clear',
13929                         cn  : [
13930                             {
13931                                 tag : 'i',
13932                                 cls: 'icon-remove'
13933                             }
13934                         ]
13935                     }
13936                 ]
13937
13938             })
13939         }
13940         
13941         if(this.multiple){
13942             combobox.cls += ' roo-select2-container-multi';
13943         }
13944          var indicator = {
13945             tag : 'i',
13946             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13947             tooltip : 'This field is required'
13948         };
13949         if (Roo.bootstrap.version == 4) {
13950             indicator = {
13951                 tag : 'i',
13952                 style : 'display:none'
13953             };
13954         }
13955         
13956         
13957         if (align ==='left' && this.fieldLabel.length) {
13958             
13959             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13960
13961             cfg.cn = [
13962                 indicator,
13963                 {
13964                     tag: 'label',
13965                     'for' :  id,
13966                     cls : 'control-label',
13967                     html : this.fieldLabel
13968
13969                 },
13970                 {
13971                     cls : "", 
13972                     cn: [
13973                         combobox
13974                     ]
13975                 }
13976
13977             ];
13978             
13979             var labelCfg = cfg.cn[1];
13980             var contentCfg = cfg.cn[2];
13981             
13982             if(this.indicatorpos == 'right'){
13983                 cfg.cn = [
13984                     {
13985                         tag: 'label',
13986                         'for' :  id,
13987                         cls : 'control-label',
13988                         cn : [
13989                             {
13990                                 tag : 'span',
13991                                 html : this.fieldLabel
13992                             },
13993                             indicator
13994                         ]
13995                     },
13996                     {
13997                         cls : "", 
13998                         cn: [
13999                             combobox
14000                         ]
14001                     }
14002
14003                 ];
14004                 
14005                 labelCfg = cfg.cn[0];
14006                 contentCfg = cfg.cn[1];
14007             }
14008             
14009             if(this.labelWidth > 12){
14010                 labelCfg.style = "width: " + this.labelWidth + 'px';
14011             }
14012             
14013             if(this.labelWidth < 13 && this.labelmd == 0){
14014                 this.labelmd = this.labelWidth;
14015             }
14016             
14017             if(this.labellg > 0){
14018                 labelCfg.cls += ' col-lg-' + this.labellg;
14019                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14020             }
14021             
14022             if(this.labelmd > 0){
14023                 labelCfg.cls += ' col-md-' + this.labelmd;
14024                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14025             }
14026             
14027             if(this.labelsm > 0){
14028                 labelCfg.cls += ' col-sm-' + this.labelsm;
14029                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14030             }
14031             
14032             if(this.labelxs > 0){
14033                 labelCfg.cls += ' col-xs-' + this.labelxs;
14034                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14035             }
14036             
14037         } else if ( this.fieldLabel.length) {
14038 //                Roo.log(" label");
14039             cfg.cn = [
14040                 indicator,
14041                {
14042                    tag: 'label',
14043                    //cls : 'input-group-addon',
14044                    html : this.fieldLabel
14045
14046                },
14047
14048                combobox
14049
14050             ];
14051             
14052             if(this.indicatorpos == 'right'){
14053                 
14054                 cfg.cn = [
14055                     {
14056                        tag: 'label',
14057                        cn : [
14058                            {
14059                                tag : 'span',
14060                                html : this.fieldLabel
14061                            },
14062                            indicator
14063                        ]
14064
14065                     },
14066                     combobox
14067
14068                 ];
14069
14070             }
14071
14072         } else {
14073             
14074 //                Roo.log(" no label && no align");
14075                 cfg = combobox
14076                      
14077                 
14078         }
14079         
14080         var settings=this;
14081         ['xs','sm','md','lg'].map(function(size){
14082             if (settings[size]) {
14083                 cfg.cls += ' col-' + size + '-' + settings[size];
14084             }
14085         });
14086         
14087         return cfg;
14088         
14089     },
14090     
14091     
14092     
14093     // private
14094     onResize : function(w, h){
14095 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14096 //        if(typeof w == 'number'){
14097 //            var x = w - this.trigger.getWidth();
14098 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14099 //            this.trigger.setStyle('left', x+'px');
14100 //        }
14101     },
14102
14103     // private
14104     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14105
14106     // private
14107     getResizeEl : function(){
14108         return this.inputEl();
14109     },
14110
14111     // private
14112     getPositionEl : function(){
14113         return this.inputEl();
14114     },
14115
14116     // private
14117     alignErrorIcon : function(){
14118         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14119     },
14120
14121     // private
14122     initEvents : function(){
14123         
14124         this.createList();
14125         
14126         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14127         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14128         if(!this.multiple && this.showToggleBtn){
14129             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14130             if(this.hideTrigger){
14131                 this.trigger.setDisplayed(false);
14132             }
14133             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14134         }
14135         
14136         if(this.multiple){
14137             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14138         }
14139         
14140         if(this.removable && !this.editable && !this.tickable){
14141             var close = this.closeTriggerEl();
14142             
14143             if(close){
14144                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14145                 close.on('click', this.removeBtnClick, this, close);
14146             }
14147         }
14148         
14149         //this.trigger.addClassOnOver('x-form-trigger-over');
14150         //this.trigger.addClassOnClick('x-form-trigger-click');
14151         
14152         //if(!this.width){
14153         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14154         //}
14155     },
14156     
14157     closeTriggerEl : function()
14158     {
14159         var close = this.el.select('.roo-combo-removable-btn', true).first();
14160         return close ? close : false;
14161     },
14162     
14163     removeBtnClick : function(e, h, el)
14164     {
14165         e.preventDefault();
14166         
14167         if(this.fireEvent("remove", this) !== false){
14168             this.reset();
14169             this.fireEvent("afterremove", this)
14170         }
14171     },
14172     
14173     createList : function()
14174     {
14175         this.list = Roo.get(document.body).createChild({
14176             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14177             cls: 'typeahead typeahead-long dropdown-menu shadow',
14178             style: 'display:none'
14179         });
14180         
14181         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14182         
14183     },
14184
14185     // private
14186     initTrigger : function(){
14187        
14188     },
14189
14190     // private
14191     onDestroy : function(){
14192         if(this.trigger){
14193             this.trigger.removeAllListeners();
14194           //  this.trigger.remove();
14195         }
14196         //if(this.wrap){
14197         //    this.wrap.remove();
14198         //}
14199         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14200     },
14201
14202     // private
14203     onFocus : function(){
14204         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14205         /*
14206         if(!this.mimicing){
14207             this.wrap.addClass('x-trigger-wrap-focus');
14208             this.mimicing = true;
14209             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14210             if(this.monitorTab){
14211                 this.el.on("keydown", this.checkTab, this);
14212             }
14213         }
14214         */
14215     },
14216
14217     // private
14218     checkTab : function(e){
14219         if(e.getKey() == e.TAB){
14220             this.triggerBlur();
14221         }
14222     },
14223
14224     // private
14225     onBlur : function(){
14226         // do nothing
14227     },
14228
14229     // private
14230     mimicBlur : function(e, t){
14231         /*
14232         if(!this.wrap.contains(t) && this.validateBlur()){
14233             this.triggerBlur();
14234         }
14235         */
14236     },
14237
14238     // private
14239     triggerBlur : function(){
14240         this.mimicing = false;
14241         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14242         if(this.monitorTab){
14243             this.el.un("keydown", this.checkTab, this);
14244         }
14245         //this.wrap.removeClass('x-trigger-wrap-focus');
14246         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14247     },
14248
14249     // private
14250     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14251     validateBlur : function(e, t){
14252         return true;
14253     },
14254
14255     // private
14256     onDisable : function(){
14257         this.inputEl().dom.disabled = true;
14258         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14259         //if(this.wrap){
14260         //    this.wrap.addClass('x-item-disabled');
14261         //}
14262     },
14263
14264     // private
14265     onEnable : function(){
14266         this.inputEl().dom.disabled = false;
14267         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14268         //if(this.wrap){
14269         //    this.el.removeClass('x-item-disabled');
14270         //}
14271     },
14272
14273     // private
14274     onShow : function(){
14275         var ae = this.getActionEl();
14276         
14277         if(ae){
14278             ae.dom.style.display = '';
14279             ae.dom.style.visibility = 'visible';
14280         }
14281     },
14282
14283     // private
14284     
14285     onHide : function(){
14286         var ae = this.getActionEl();
14287         ae.dom.style.display = 'none';
14288     },
14289
14290     /**
14291      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14292      * by an implementing function.
14293      * @method
14294      * @param {EventObject} e
14295      */
14296     onTriggerClick : Roo.emptyFn
14297 });
14298  
14299 /*
14300 * Licence: LGPL
14301 */
14302
14303 /**
14304  * @class Roo.bootstrap.form.CardUploader
14305  * @extends Roo.bootstrap.Button
14306  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14307  * @cfg {Number} errorTimeout default 3000
14308  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14309  * @cfg {Array}  html The button text.
14310
14311  *
14312  * @constructor
14313  * Create a new CardUploader
14314  * @param {Object} config The config object
14315  */
14316
14317 Roo.bootstrap.form.CardUploader = function(config){
14318     
14319  
14320     
14321     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14322     
14323     
14324     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14325         return r.data.id
14326      });
14327     
14328      this.addEvents({
14329          // raw events
14330         /**
14331          * @event preview
14332          * When a image is clicked on - and needs to display a slideshow or similar..
14333          * @param {Roo.bootstrap.Card} this
14334          * @param {Object} The image information data 
14335          *
14336          */
14337         'preview' : true,
14338          /**
14339          * @event download
14340          * When a the download link is clicked
14341          * @param {Roo.bootstrap.Card} this
14342          * @param {Object} The image information data  contains 
14343          */
14344         'download' : true
14345         
14346     });
14347 };
14348  
14349 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14350     
14351      
14352     errorTimeout : 3000,
14353      
14354     images : false,
14355    
14356     fileCollection : false,
14357     allowBlank : true,
14358     
14359     getAutoCreate : function()
14360     {
14361         
14362         var cfg =  {
14363             cls :'form-group' ,
14364             cn : [
14365                
14366                 {
14367                     tag: 'label',
14368                    //cls : 'input-group-addon',
14369                     html : this.fieldLabel
14370
14371                 },
14372
14373                 {
14374                     tag: 'input',
14375                     type : 'hidden',
14376                     name : this.name,
14377                     value : this.value,
14378                     cls : 'd-none  form-control'
14379                 },
14380                 
14381                 {
14382                     tag: 'input',
14383                     multiple : 'multiple',
14384                     type : 'file',
14385                     cls : 'd-none  roo-card-upload-selector'
14386                 },
14387                 
14388                 {
14389                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14390                 },
14391                 {
14392                     cls : 'card-columns roo-card-uploader-container'
14393                 }
14394
14395             ]
14396         };
14397            
14398          
14399         return cfg;
14400     },
14401     
14402     getChildContainer : function() /// what children are added to.
14403     {
14404         return this.containerEl;
14405     },
14406    
14407     getButtonContainer : function() /// what children are added to.
14408     {
14409         return this.el.select(".roo-card-uploader-button-container").first();
14410     },
14411    
14412     initEvents : function()
14413     {
14414         
14415         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14416         
14417         var t = this;
14418         this.addxtype({
14419             xns: Roo.bootstrap,
14420
14421             xtype : 'Button',
14422             container_method : 'getButtonContainer' ,            
14423             html :  this.html, // fix changable?
14424             cls : 'w-100 ',
14425             listeners : {
14426                 'click' : function(btn, e) {
14427                     t.onClick(e);
14428                 }
14429             }
14430         });
14431         
14432         
14433         
14434         
14435         this.urlAPI = (window.createObjectURL && window) || 
14436                                 (window.URL && URL.revokeObjectURL && URL) || 
14437                                 (window.webkitURL && webkitURL);
14438                         
14439          
14440          
14441          
14442         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14443         
14444         this.selectorEl.on('change', this.onFileSelected, this);
14445         if (this.images) {
14446             var t = this;
14447             this.images.forEach(function(img) {
14448                 t.addCard(img)
14449             });
14450             this.images = false;
14451         }
14452         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14453          
14454        
14455     },
14456     
14457    
14458     onClick : function(e)
14459     {
14460         e.preventDefault();
14461          
14462         this.selectorEl.dom.click();
14463          
14464     },
14465     
14466     onFileSelected : function(e)
14467     {
14468         e.preventDefault();
14469         
14470         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14471             return;
14472         }
14473         
14474         Roo.each(this.selectorEl.dom.files, function(file){    
14475             this.addFile(file);
14476         }, this);
14477          
14478     },
14479     
14480       
14481     
14482       
14483     
14484     addFile : function(file)
14485     {
14486            
14487         if(typeof(file) === 'string'){
14488             throw "Add file by name?"; // should not happen
14489             return;
14490         }
14491         
14492         if(!file || !this.urlAPI){
14493             return;
14494         }
14495         
14496         // file;
14497         // file.type;
14498         
14499         var _this = this;
14500         
14501         
14502         var url = _this.urlAPI.createObjectURL( file);
14503            
14504         this.addCard({
14505             id : Roo.bootstrap.form.CardUploader.ID--,
14506             is_uploaded : false,
14507             src : url,
14508             srcfile : file,
14509             title : file.name,
14510             mimetype : file.type,
14511             preview : false,
14512             is_deleted : 0
14513         });
14514         
14515     },
14516     
14517     /**
14518      * addCard - add an Attachment to the uploader
14519      * @param data - the data about the image to upload
14520      *
14521      * {
14522           id : 123
14523           title : "Title of file",
14524           is_uploaded : false,
14525           src : "http://.....",
14526           srcfile : { the File upload object },
14527           mimetype : file.type,
14528           preview : false,
14529           is_deleted : 0
14530           .. any other data...
14531         }
14532      *
14533      * 
14534     */
14535     
14536     addCard : function (data)
14537     {
14538         // hidden input element?
14539         // if the file is not an image...
14540         //then we need to use something other that and header_image
14541         var t = this;
14542         //   remove.....
14543         var footer = [
14544             {
14545                 xns : Roo.bootstrap,
14546                 xtype : 'CardFooter',
14547                  items: [
14548                     {
14549                         xns : Roo.bootstrap,
14550                         xtype : 'Element',
14551                         cls : 'd-flex',
14552                         items : [
14553                             
14554                             {
14555                                 xns : Roo.bootstrap,
14556                                 xtype : 'Button',
14557                                 html : String.format("<small>{0}</small>", data.title),
14558                                 cls : 'col-10 text-left',
14559                                 size: 'sm',
14560                                 weight: 'link',
14561                                 fa : 'download',
14562                                 listeners : {
14563                                     click : function() {
14564                                      
14565                                         t.fireEvent( "download", t, data );
14566                                     }
14567                                 }
14568                             },
14569                           
14570                             {
14571                                 xns : Roo.bootstrap,
14572                                 xtype : 'Button',
14573                                 style: 'max-height: 28px; ',
14574                                 size : 'sm',
14575                                 weight: 'danger',
14576                                 cls : 'col-2',
14577                                 fa : 'times',
14578                                 listeners : {
14579                                     click : function() {
14580                                         t.removeCard(data.id)
14581                                     }
14582                                 }
14583                             }
14584                         ]
14585                     }
14586                     
14587                 ] 
14588             }
14589             
14590         ];
14591         
14592         var cn = this.addxtype(
14593             {
14594                  
14595                 xns : Roo.bootstrap,
14596                 xtype : 'Card',
14597                 closeable : true,
14598                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14599                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14600                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14601                 data : data,
14602                 html : false,
14603                  
14604                 items : footer,
14605                 initEvents : function() {
14606                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14607                     var card = this;
14608                     this.imgEl = this.el.select('.card-img-top').first();
14609                     if (this.imgEl) {
14610                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14611                         this.imgEl.set({ 'pointer' : 'cursor' });
14612                                   
14613                     }
14614                     this.getCardFooter().addClass('p-1');
14615                     
14616                   
14617                 }
14618                 
14619             }
14620         );
14621         // dont' really need ot update items.
14622         // this.items.push(cn);
14623         this.fileCollection.add(cn);
14624         
14625         if (!data.srcfile) {
14626             this.updateInput();
14627             return;
14628         }
14629             
14630         var _t = this;
14631         var reader = new FileReader();
14632         reader.addEventListener("load", function() {  
14633             data.srcdata =  reader.result;
14634             _t.updateInput();
14635         });
14636         reader.readAsDataURL(data.srcfile);
14637         
14638         
14639         
14640     },
14641     removeCard : function(id)
14642     {
14643         
14644         var card  = this.fileCollection.get(id);
14645         card.data.is_deleted = 1;
14646         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14647         //this.fileCollection.remove(card);
14648         //this.items = this.items.filter(function(e) { return e != card });
14649         // dont' really need ot update items.
14650         card.el.dom.parentNode.removeChild(card.el.dom);
14651         this.updateInput();
14652
14653         
14654     },
14655     reset: function()
14656     {
14657         this.fileCollection.each(function(card) {
14658             if (card.el.dom && card.el.dom.parentNode) {
14659                 card.el.dom.parentNode.removeChild(card.el.dom);
14660             }
14661         });
14662         this.fileCollection.clear();
14663         this.updateInput();
14664     },
14665     
14666     updateInput : function()
14667     {
14668          var data = [];
14669         this.fileCollection.each(function(e) {
14670             data.push(e.data);
14671             
14672         });
14673         this.inputEl().dom.value = JSON.stringify(data);
14674         
14675         
14676         
14677     }
14678     
14679     
14680 });
14681
14682
14683 Roo.bootstrap.form.CardUploader.ID = -1;/*
14684  * Based on:
14685  * Ext JS Library 1.1.1
14686  * Copyright(c) 2006-2007, Ext JS, LLC.
14687  *
14688  * Originally Released Under LGPL - original licence link has changed is not relivant.
14689  *
14690  * Fork - LGPL
14691  * <script type="text/javascript">
14692  */
14693
14694
14695 /**
14696  * @class Roo.data.SortTypes
14697  * @static
14698  * Defines the default sorting (casting?) comparison functions used when sorting data.
14699  */
14700 Roo.data.SortTypes = {
14701     /**
14702      * Default sort that does nothing
14703      * @param {Mixed} s The value being converted
14704      * @return {Mixed} The comparison value
14705      */
14706     none : function(s){
14707         return s;
14708     },
14709     
14710     /**
14711      * The regular expression used to strip tags
14712      * @type {RegExp}
14713      * @property
14714      */
14715     stripTagsRE : /<\/?[^>]+>/gi,
14716     
14717     /**
14718      * Strips all HTML tags to sort on text only
14719      * @param {Mixed} s The value being converted
14720      * @return {String} The comparison value
14721      */
14722     asText : function(s){
14723         return String(s).replace(this.stripTagsRE, "");
14724     },
14725     
14726     /**
14727      * Strips all HTML tags to sort on text only - Case insensitive
14728      * @param {Mixed} s The value being converted
14729      * @return {String} The comparison value
14730      */
14731     asUCText : function(s){
14732         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14733     },
14734     
14735     /**
14736      * Case insensitive string
14737      * @param {Mixed} s The value being converted
14738      * @return {String} The comparison value
14739      */
14740     asUCString : function(s) {
14741         return String(s).toUpperCase();
14742     },
14743     
14744     /**
14745      * Date sorting
14746      * @param {Mixed} s The value being converted
14747      * @return {Number} The comparison value
14748      */
14749     asDate : function(s) {
14750         if(!s){
14751             return 0;
14752         }
14753         if(s instanceof Date){
14754             return s.getTime();
14755         }
14756         return Date.parse(String(s));
14757     },
14758     
14759     /**
14760      * Float sorting
14761      * @param {Mixed} s The value being converted
14762      * @return {Float} The comparison value
14763      */
14764     asFloat : function(s) {
14765         var val = parseFloat(String(s).replace(/,/g, ""));
14766         if(isNaN(val)) {
14767             val = 0;
14768         }
14769         return val;
14770     },
14771     
14772     /**
14773      * Integer sorting
14774      * @param {Mixed} s The value being converted
14775      * @return {Number} The comparison value
14776      */
14777     asInt : function(s) {
14778         var val = parseInt(String(s).replace(/,/g, ""));
14779         if(isNaN(val)) {
14780             val = 0;
14781         }
14782         return val;
14783     }
14784 };/*
14785  * Based on:
14786  * Ext JS Library 1.1.1
14787  * Copyright(c) 2006-2007, Ext JS, LLC.
14788  *
14789  * Originally Released Under LGPL - original licence link has changed is not relivant.
14790  *
14791  * Fork - LGPL
14792  * <script type="text/javascript">
14793  */
14794
14795 /**
14796 * @class Roo.data.Record
14797  * Instances of this class encapsulate both record <em>definition</em> information, and record
14798  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14799  * to access Records cached in an {@link Roo.data.Store} object.<br>
14800  * <p>
14801  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14802  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14803  * objects.<br>
14804  * <p>
14805  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14806  * @constructor
14807  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14808  * {@link #create}. The parameters are the same.
14809  * @param {Array} data An associative Array of data values keyed by the field name.
14810  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14811  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14812  * not specified an integer id is generated.
14813  */
14814 Roo.data.Record = function(data, id){
14815     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14816     this.data = data;
14817 };
14818
14819 /**
14820  * Generate a constructor for a specific record layout.
14821  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14822  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14823  * Each field definition object may contain the following properties: <ul>
14824  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14825  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14826  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14827  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14828  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14829  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14830  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14831  * this may be omitted.</p></li>
14832  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14833  * <ul><li>auto (Default, implies no conversion)</li>
14834  * <li>string</li>
14835  * <li>int</li>
14836  * <li>float</li>
14837  * <li>boolean</li>
14838  * <li>date</li></ul></p></li>
14839  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14840  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14841  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14842  * by the Reader into an object that will be stored in the Record. It is passed the
14843  * following parameters:<ul>
14844  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14845  * </ul></p></li>
14846  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14847  * </ul>
14848  * <br>usage:<br><pre><code>
14849 var TopicRecord = Roo.data.Record.create(
14850     {name: 'title', mapping: 'topic_title'},
14851     {name: 'author', mapping: 'username'},
14852     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14853     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14854     {name: 'lastPoster', mapping: 'user2'},
14855     {name: 'excerpt', mapping: 'post_text'}
14856 );
14857
14858 var myNewRecord = new TopicRecord({
14859     title: 'Do my job please',
14860     author: 'noobie',
14861     totalPosts: 1,
14862     lastPost: new Date(),
14863     lastPoster: 'Animal',
14864     excerpt: 'No way dude!'
14865 });
14866 myStore.add(myNewRecord);
14867 </code></pre>
14868  * @method create
14869  * @static
14870  */
14871 Roo.data.Record.create = function(o){
14872     var f = function(){
14873         f.superclass.constructor.apply(this, arguments);
14874     };
14875     Roo.extend(f, Roo.data.Record);
14876     var p = f.prototype;
14877     p.fields = new Roo.util.MixedCollection(false, function(field){
14878         return field.name;
14879     });
14880     for(var i = 0, len = o.length; i < len; i++){
14881         p.fields.add(new Roo.data.Field(o[i]));
14882     }
14883     f.getField = function(name){
14884         return p.fields.get(name);  
14885     };
14886     return f;
14887 };
14888
14889 Roo.data.Record.AUTO_ID = 1000;
14890 Roo.data.Record.EDIT = 'edit';
14891 Roo.data.Record.REJECT = 'reject';
14892 Roo.data.Record.COMMIT = 'commit';
14893
14894 Roo.data.Record.prototype = {
14895     /**
14896      * Readonly flag - true if this record has been modified.
14897      * @type Boolean
14898      */
14899     dirty : false,
14900     editing : false,
14901     error: null,
14902     modified: null,
14903
14904     // private
14905     join : function(store){
14906         this.store = store;
14907     },
14908
14909     /**
14910      * Set the named field to the specified value.
14911      * @param {String} name The name of the field to set.
14912      * @param {Object} value The value to set the field to.
14913      */
14914     set : function(name, value){
14915         if(this.data[name] == value){
14916             return;
14917         }
14918         this.dirty = true;
14919         if(!this.modified){
14920             this.modified = {};
14921         }
14922         if(typeof this.modified[name] == 'undefined'){
14923             this.modified[name] = this.data[name];
14924         }
14925         this.data[name] = value;
14926         if(!this.editing && this.store){
14927             this.store.afterEdit(this);
14928         }       
14929     },
14930
14931     /**
14932      * Get the value of the named field.
14933      * @param {String} name The name of the field to get the value of.
14934      * @return {Object} The value of the field.
14935      */
14936     get : function(name){
14937         return this.data[name]; 
14938     },
14939
14940     // private
14941     beginEdit : function(){
14942         this.editing = true;
14943         this.modified = {}; 
14944     },
14945
14946     // private
14947     cancelEdit : function(){
14948         this.editing = false;
14949         delete this.modified;
14950     },
14951
14952     // private
14953     endEdit : function(){
14954         this.editing = false;
14955         if(this.dirty && this.store){
14956             this.store.afterEdit(this);
14957         }
14958     },
14959
14960     /**
14961      * Usually called by the {@link Roo.data.Store} which owns the Record.
14962      * Rejects all changes made to the Record since either creation, or the last commit operation.
14963      * Modified fields are reverted to their original values.
14964      * <p>
14965      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14966      * of reject operations.
14967      */
14968     reject : function(){
14969         var m = this.modified;
14970         for(var n in m){
14971             if(typeof m[n] != "function"){
14972                 this.data[n] = m[n];
14973             }
14974         }
14975         this.dirty = false;
14976         delete this.modified;
14977         this.editing = false;
14978         if(this.store){
14979             this.store.afterReject(this);
14980         }
14981     },
14982
14983     /**
14984      * Usually called by the {@link Roo.data.Store} which owns the Record.
14985      * Commits all changes made to the Record since either creation, or the last commit operation.
14986      * <p>
14987      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14988      * of commit operations.
14989      */
14990     commit : function(){
14991         this.dirty = false;
14992         delete this.modified;
14993         this.editing = false;
14994         if(this.store){
14995             this.store.afterCommit(this);
14996         }
14997     },
14998
14999     // private
15000     hasError : function(){
15001         return this.error != null;
15002     },
15003
15004     // private
15005     clearError : function(){
15006         this.error = null;
15007     },
15008
15009     /**
15010      * Creates a copy of this record.
15011      * @param {String} id (optional) A new record id if you don't want to use this record's id
15012      * @return {Record}
15013      */
15014     copy : function(newId) {
15015         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15016     }
15017 };/*
15018  * Based on:
15019  * Ext JS Library 1.1.1
15020  * Copyright(c) 2006-2007, Ext JS, LLC.
15021  *
15022  * Originally Released Under LGPL - original licence link has changed is not relivant.
15023  *
15024  * Fork - LGPL
15025  * <script type="text/javascript">
15026  */
15027
15028
15029
15030 /**
15031  * @class Roo.data.Store
15032  * @extends Roo.util.Observable
15033  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15034  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15035  * <p>
15036  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15037  * has no knowledge of the format of the data returned by the Proxy.<br>
15038  * <p>
15039  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15040  * instances from the data object. These records are cached and made available through accessor functions.
15041  * @constructor
15042  * Creates a new Store.
15043  * @param {Object} config A config object containing the objects needed for the Store to access data,
15044  * and read the data into Records.
15045  */
15046 Roo.data.Store = function(config){
15047     this.data = new Roo.util.MixedCollection(false);
15048     this.data.getKey = function(o){
15049         return o.id;
15050     };
15051     this.baseParams = {};
15052     // private
15053     this.paramNames = {
15054         "start" : "start",
15055         "limit" : "limit",
15056         "sort" : "sort",
15057         "dir" : "dir",
15058         "multisort" : "_multisort"
15059     };
15060
15061     if(config && config.data){
15062         this.inlineData = config.data;
15063         delete config.data;
15064     }
15065
15066     Roo.apply(this, config);
15067     
15068     if(this.reader){ // reader passed
15069         this.reader = Roo.factory(this.reader, Roo.data);
15070         this.reader.xmodule = this.xmodule || false;
15071         if(!this.recordType){
15072             this.recordType = this.reader.recordType;
15073         }
15074         if(this.reader.onMetaChange){
15075             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15076         }
15077     }
15078
15079     if(this.recordType){
15080         this.fields = this.recordType.prototype.fields;
15081     }
15082     this.modified = [];
15083
15084     this.addEvents({
15085         /**
15086          * @event datachanged
15087          * Fires when the data cache has changed, and a widget which is using this Store
15088          * as a Record cache should refresh its view.
15089          * @param {Store} this
15090          */
15091         datachanged : true,
15092         /**
15093          * @event metachange
15094          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15095          * @param {Store} this
15096          * @param {Object} meta The JSON metadata
15097          */
15098         metachange : true,
15099         /**
15100          * @event add
15101          * Fires when Records have been added to the Store
15102          * @param {Store} this
15103          * @param {Roo.data.Record[]} records The array of Records added
15104          * @param {Number} index The index at which the record(s) were added
15105          */
15106         add : true,
15107         /**
15108          * @event remove
15109          * Fires when a Record has been removed from the Store
15110          * @param {Store} this
15111          * @param {Roo.data.Record} record The Record that was removed
15112          * @param {Number} index The index at which the record was removed
15113          */
15114         remove : true,
15115         /**
15116          * @event update
15117          * Fires when a Record has been updated
15118          * @param {Store} this
15119          * @param {Roo.data.Record} record The Record that was updated
15120          * @param {String} operation The update operation being performed.  Value may be one of:
15121          * <pre><code>
15122  Roo.data.Record.EDIT
15123  Roo.data.Record.REJECT
15124  Roo.data.Record.COMMIT
15125          * </code></pre>
15126          */
15127         update : true,
15128         /**
15129          * @event clear
15130          * Fires when the data cache has been cleared.
15131          * @param {Store} this
15132          */
15133         clear : true,
15134         /**
15135          * @event beforeload
15136          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15137          * the load action will be canceled.
15138          * @param {Store} this
15139          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15140          */
15141         beforeload : true,
15142         /**
15143          * @event beforeloadadd
15144          * Fires after a new set of Records has been loaded.
15145          * @param {Store} this
15146          * @param {Roo.data.Record[]} records The Records that were loaded
15147          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15148          */
15149         beforeloadadd : true,
15150         /**
15151          * @event load
15152          * Fires after a new set of Records has been loaded, before they are added to the store.
15153          * @param {Store} this
15154          * @param {Roo.data.Record[]} records The Records that were loaded
15155          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15156          * @params {Object} return from reader
15157          */
15158         load : true,
15159         /**
15160          * @event loadexception
15161          * Fires if an exception occurs in the Proxy during loading.
15162          * Called with the signature of the Proxy's "loadexception" event.
15163          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15164          * 
15165          * @param {Proxy} 
15166          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15167          * @param {Object} load options 
15168          * @param {Object} jsonData from your request (normally this contains the Exception)
15169          */
15170         loadexception : true
15171     });
15172     
15173     if(this.proxy){
15174         this.proxy = Roo.factory(this.proxy, Roo.data);
15175         this.proxy.xmodule = this.xmodule || false;
15176         this.relayEvents(this.proxy,  ["loadexception"]);
15177     }
15178     this.sortToggle = {};
15179     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15180
15181     Roo.data.Store.superclass.constructor.call(this);
15182
15183     if(this.inlineData){
15184         this.loadData(this.inlineData);
15185         delete this.inlineData;
15186     }
15187 };
15188
15189 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15190      /**
15191     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15192     * without a remote query - used by combo/forms at present.
15193     */
15194     
15195     /**
15196     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15197     */
15198     /**
15199     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15200     */
15201     /**
15202     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15203     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15204     */
15205     /**
15206     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15207     * on any HTTP request
15208     */
15209     /**
15210     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15211     */
15212     /**
15213     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15214     */
15215     multiSort: false,
15216     /**
15217     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15218     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15219     */
15220     remoteSort : false,
15221
15222     /**
15223     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15224      * loaded or when a record is removed. (defaults to false).
15225     */
15226     pruneModifiedRecords : false,
15227
15228     // private
15229     lastOptions : null,
15230
15231     /**
15232      * Add Records to the Store and fires the add event.
15233      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15234      */
15235     add : function(records){
15236         records = [].concat(records);
15237         for(var i = 0, len = records.length; i < len; i++){
15238             records[i].join(this);
15239         }
15240         var index = this.data.length;
15241         this.data.addAll(records);
15242         this.fireEvent("add", this, records, index);
15243     },
15244
15245     /**
15246      * Remove a Record from the Store and fires the remove event.
15247      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15248      */
15249     remove : function(record){
15250         var index = this.data.indexOf(record);
15251         this.data.removeAt(index);
15252  
15253         if(this.pruneModifiedRecords){
15254             this.modified.remove(record);
15255         }
15256         this.fireEvent("remove", this, record, index);
15257     },
15258
15259     /**
15260      * Remove all Records from the Store and fires the clear event.
15261      */
15262     removeAll : function(){
15263         this.data.clear();
15264         if(this.pruneModifiedRecords){
15265             this.modified = [];
15266         }
15267         this.fireEvent("clear", this);
15268     },
15269
15270     /**
15271      * Inserts Records to the Store at the given index and fires the add event.
15272      * @param {Number} index The start index at which to insert the passed Records.
15273      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15274      */
15275     insert : function(index, records){
15276         records = [].concat(records);
15277         for(var i = 0, len = records.length; i < len; i++){
15278             this.data.insert(index, records[i]);
15279             records[i].join(this);
15280         }
15281         this.fireEvent("add", this, records, index);
15282     },
15283
15284     /**
15285      * Get the index within the cache of the passed Record.
15286      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15287      * @return {Number} The index of the passed Record. Returns -1 if not found.
15288      */
15289     indexOf : function(record){
15290         return this.data.indexOf(record);
15291     },
15292
15293     /**
15294      * Get the index within the cache of the Record with the passed id.
15295      * @param {String} id The id of the Record to find.
15296      * @return {Number} The index of the Record. Returns -1 if not found.
15297      */
15298     indexOfId : function(id){
15299         return this.data.indexOfKey(id);
15300     },
15301
15302     /**
15303      * Get the Record with the specified id.
15304      * @param {String} id The id of the Record to find.
15305      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15306      */
15307     getById : function(id){
15308         return this.data.key(id);
15309     },
15310
15311     /**
15312      * Get the Record at the specified index.
15313      * @param {Number} index The index of the Record to find.
15314      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15315      */
15316     getAt : function(index){
15317         return this.data.itemAt(index);
15318     },
15319
15320     /**
15321      * Returns a range of Records between specified indices.
15322      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15323      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15324      * @return {Roo.data.Record[]} An array of Records
15325      */
15326     getRange : function(start, end){
15327         return this.data.getRange(start, end);
15328     },
15329
15330     // private
15331     storeOptions : function(o){
15332         o = Roo.apply({}, o);
15333         delete o.callback;
15334         delete o.scope;
15335         this.lastOptions = o;
15336     },
15337
15338     /**
15339      * Loads the Record cache from the configured Proxy using the configured Reader.
15340      * <p>
15341      * If using remote paging, then the first load call must specify the <em>start</em>
15342      * and <em>limit</em> properties in the options.params property to establish the initial
15343      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15344      * <p>
15345      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15346      * and this call will return before the new data has been loaded. Perform any post-processing
15347      * in a callback function, or in a "load" event handler.</strong>
15348      * <p>
15349      * @param {Object} options An object containing properties which control loading options:<ul>
15350      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15351      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15352      * passed the following arguments:<ul>
15353      * <li>r : Roo.data.Record[]</li>
15354      * <li>options: Options object from the load call</li>
15355      * <li>success: Boolean success indicator</li></ul></li>
15356      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15357      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15358      * </ul>
15359      */
15360     load : function(options){
15361         options = options || {};
15362         if(this.fireEvent("beforeload", this, options) !== false){
15363             this.storeOptions(options);
15364             var p = Roo.apply(options.params || {}, this.baseParams);
15365             // if meta was not loaded from remote source.. try requesting it.
15366             if (!this.reader.metaFromRemote) {
15367                 p._requestMeta = 1;
15368             }
15369             if(this.sortInfo && this.remoteSort){
15370                 var pn = this.paramNames;
15371                 p[pn["sort"]] = this.sortInfo.field;
15372                 p[pn["dir"]] = this.sortInfo.direction;
15373             }
15374             if (this.multiSort) {
15375                 var pn = this.paramNames;
15376                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15377             }
15378             
15379             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15380         }
15381     },
15382
15383     /**
15384      * Reloads the Record cache from the configured Proxy using the configured Reader and
15385      * the options from the last load operation performed.
15386      * @param {Object} options (optional) An object containing properties which may override the options
15387      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15388      * the most recently used options are reused).
15389      */
15390     reload : function(options){
15391         this.load(Roo.applyIf(options||{}, this.lastOptions));
15392     },
15393
15394     // private
15395     // Called as a callback by the Reader during a load operation.
15396     loadRecords : function(o, options, success){
15397          
15398         if(!o){
15399             if(success !== false){
15400                 this.fireEvent("load", this, [], options, o);
15401             }
15402             if(options.callback){
15403                 options.callback.call(options.scope || this, [], options, false);
15404             }
15405             return;
15406         }
15407         // if data returned failure - throw an exception.
15408         if (o.success === false) {
15409             // show a message if no listener is registered.
15410             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15411                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15412             }
15413             // loadmask wil be hooked into this..
15414             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15415             return;
15416         }
15417         var r = o.records, t = o.totalRecords || r.length;
15418         
15419         this.fireEvent("beforeloadadd", this, r, options, o);
15420         
15421         if(!options || options.add !== true){
15422             if(this.pruneModifiedRecords){
15423                 this.modified = [];
15424             }
15425             for(var i = 0, len = r.length; i < len; i++){
15426                 r[i].join(this);
15427             }
15428             if(this.snapshot){
15429                 this.data = this.snapshot;
15430                 delete this.snapshot;
15431             }
15432             this.data.clear();
15433             this.data.addAll(r);
15434             this.totalLength = t;
15435             this.applySort();
15436             this.fireEvent("datachanged", this);
15437         }else{
15438             this.totalLength = Math.max(t, this.data.length+r.length);
15439             this.add(r);
15440         }
15441         
15442         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15443                 
15444             var e = new Roo.data.Record({});
15445
15446             e.set(this.parent.displayField, this.parent.emptyTitle);
15447             e.set(this.parent.valueField, '');
15448
15449             this.insert(0, e);
15450         }
15451             
15452         this.fireEvent("load", this, r, options, o);
15453         if(options.callback){
15454             options.callback.call(options.scope || this, r, options, true);
15455         }
15456     },
15457
15458
15459     /**
15460      * Loads data from a passed data block. A Reader which understands the format of the data
15461      * must have been configured in the constructor.
15462      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15463      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15464      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15465      */
15466     loadData : function(o, append){
15467         var r = this.reader.readRecords(o);
15468         this.loadRecords(r, {add: append}, true);
15469     },
15470     
15471      /**
15472      * using 'cn' the nested child reader read the child array into it's child stores.
15473      * @param {Object} rec The record with a 'children array
15474      */
15475     loadDataFromChildren : function(rec)
15476     {
15477         this.loadData(this.reader.toLoadData(rec));
15478     },
15479     
15480
15481     /**
15482      * Gets the number of cached records.
15483      * <p>
15484      * <em>If using paging, this may not be the total size of the dataset. If the data object
15485      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15486      * the data set size</em>
15487      */
15488     getCount : function(){
15489         return this.data.length || 0;
15490     },
15491
15492     /**
15493      * Gets the total number of records in the dataset as returned by the server.
15494      * <p>
15495      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15496      * the dataset size</em>
15497      */
15498     getTotalCount : function(){
15499         return this.totalLength || 0;
15500     },
15501
15502     /**
15503      * Returns the sort state of the Store as an object with two properties:
15504      * <pre><code>
15505  field {String} The name of the field by which the Records are sorted
15506  direction {String} The sort order, "ASC" or "DESC"
15507      * </code></pre>
15508      */
15509     getSortState : function(){
15510         return this.sortInfo;
15511     },
15512
15513     // private
15514     applySort : function(){
15515         if(this.sortInfo && !this.remoteSort){
15516             var s = this.sortInfo, f = s.field;
15517             var st = this.fields.get(f).sortType;
15518             var fn = function(r1, r2){
15519                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15520                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15521             };
15522             this.data.sort(s.direction, fn);
15523             if(this.snapshot && this.snapshot != this.data){
15524                 this.snapshot.sort(s.direction, fn);
15525             }
15526         }
15527     },
15528
15529     /**
15530      * Sets the default sort column and order to be used by the next load operation.
15531      * @param {String} fieldName The name of the field to sort by.
15532      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15533      */
15534     setDefaultSort : function(field, dir){
15535         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15536     },
15537
15538     /**
15539      * Sort the Records.
15540      * If remote sorting is used, the sort is performed on the server, and the cache is
15541      * reloaded. If local sorting is used, the cache is sorted internally.
15542      * @param {String} fieldName The name of the field to sort by.
15543      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15544      */
15545     sort : function(fieldName, dir){
15546         var f = this.fields.get(fieldName);
15547         if(!dir){
15548             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15549             
15550             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15551                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15552             }else{
15553                 dir = f.sortDir;
15554             }
15555         }
15556         this.sortToggle[f.name] = dir;
15557         this.sortInfo = {field: f.name, direction: dir};
15558         if(!this.remoteSort){
15559             this.applySort();
15560             this.fireEvent("datachanged", this);
15561         }else{
15562             this.load(this.lastOptions);
15563         }
15564     },
15565
15566     /**
15567      * Calls the specified function for each of the Records in the cache.
15568      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15569      * Returning <em>false</em> aborts and exits the iteration.
15570      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15571      */
15572     each : function(fn, scope){
15573         this.data.each(fn, scope);
15574     },
15575
15576     /**
15577      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15578      * (e.g., during paging).
15579      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15580      */
15581     getModifiedRecords : function(){
15582         return this.modified;
15583     },
15584
15585     // private
15586     createFilterFn : function(property, value, anyMatch){
15587         if(!value.exec){ // not a regex
15588             value = String(value);
15589             if(value.length == 0){
15590                 return false;
15591             }
15592             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15593         }
15594         return function(r){
15595             return value.test(r.data[property]);
15596         };
15597     },
15598
15599     /**
15600      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15601      * @param {String} property A field on your records
15602      * @param {Number} start The record index to start at (defaults to 0)
15603      * @param {Number} end The last record index to include (defaults to length - 1)
15604      * @return {Number} The sum
15605      */
15606     sum : function(property, start, end){
15607         var rs = this.data.items, v = 0;
15608         start = start || 0;
15609         end = (end || end === 0) ? end : rs.length-1;
15610
15611         for(var i = start; i <= end; i++){
15612             v += (rs[i].data[property] || 0);
15613         }
15614         return v;
15615     },
15616
15617     /**
15618      * Filter the records by a specified property.
15619      * @param {String} field A field on your records
15620      * @param {String/RegExp} value Either a string that the field
15621      * should start with or a RegExp to test against the field
15622      * @param {Boolean} anyMatch True to match any part not just the beginning
15623      */
15624     filter : function(property, value, anyMatch){
15625         var fn = this.createFilterFn(property, value, anyMatch);
15626         return fn ? this.filterBy(fn) : this.clearFilter();
15627     },
15628
15629     /**
15630      * Filter by a function. The specified function will be called with each
15631      * record in this data source. If the function returns true the record is included,
15632      * otherwise it is filtered.
15633      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15634      * @param {Object} scope (optional) The scope of the function (defaults to this)
15635      */
15636     filterBy : function(fn, scope){
15637         this.snapshot = this.snapshot || this.data;
15638         this.data = this.queryBy(fn, scope||this);
15639         this.fireEvent("datachanged", this);
15640     },
15641
15642     /**
15643      * Query the records by a specified property.
15644      * @param {String} field A field on your records
15645      * @param {String/RegExp} value Either a string that the field
15646      * should start with or a RegExp to test against the field
15647      * @param {Boolean} anyMatch True to match any part not just the beginning
15648      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15649      */
15650     query : function(property, value, anyMatch){
15651         var fn = this.createFilterFn(property, value, anyMatch);
15652         return fn ? this.queryBy(fn) : this.data.clone();
15653     },
15654
15655     /**
15656      * Query by a function. The specified function will be called with each
15657      * record in this data source. If the function returns true the record is included
15658      * in the results.
15659      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15660      * @param {Object} scope (optional) The scope of the function (defaults to this)
15661       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15662      **/
15663     queryBy : function(fn, scope){
15664         var data = this.snapshot || this.data;
15665         return data.filterBy(fn, scope||this);
15666     },
15667
15668     /**
15669      * Collects unique values for a particular dataIndex from this store.
15670      * @param {String} dataIndex The property to collect
15671      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15672      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15673      * @return {Array} An array of the unique values
15674      **/
15675     collect : function(dataIndex, allowNull, bypassFilter){
15676         var d = (bypassFilter === true && this.snapshot) ?
15677                 this.snapshot.items : this.data.items;
15678         var v, sv, r = [], l = {};
15679         for(var i = 0, len = d.length; i < len; i++){
15680             v = d[i].data[dataIndex];
15681             sv = String(v);
15682             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15683                 l[sv] = true;
15684                 r[r.length] = v;
15685             }
15686         }
15687         return r;
15688     },
15689
15690     /**
15691      * Revert to a view of the Record cache with no filtering applied.
15692      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15693      */
15694     clearFilter : function(suppressEvent){
15695         if(this.snapshot && this.snapshot != this.data){
15696             this.data = this.snapshot;
15697             delete this.snapshot;
15698             if(suppressEvent !== true){
15699                 this.fireEvent("datachanged", this);
15700             }
15701         }
15702     },
15703
15704     // private
15705     afterEdit : function(record){
15706         if(this.modified.indexOf(record) == -1){
15707             this.modified.push(record);
15708         }
15709         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15710     },
15711     
15712     // private
15713     afterReject : function(record){
15714         this.modified.remove(record);
15715         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15716     },
15717
15718     // private
15719     afterCommit : function(record){
15720         this.modified.remove(record);
15721         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15722     },
15723
15724     /**
15725      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15726      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15727      */
15728     commitChanges : function(){
15729         var m = this.modified.slice(0);
15730         this.modified = [];
15731         for(var i = 0, len = m.length; i < len; i++){
15732             m[i].commit();
15733         }
15734     },
15735
15736     /**
15737      * Cancel outstanding changes on all changed records.
15738      */
15739     rejectChanges : function(){
15740         var m = this.modified.slice(0);
15741         this.modified = [];
15742         for(var i = 0, len = m.length; i < len; i++){
15743             m[i].reject();
15744         }
15745     },
15746
15747     onMetaChange : function(meta, rtype, o){
15748         this.recordType = rtype;
15749         this.fields = rtype.prototype.fields;
15750         delete this.snapshot;
15751         this.sortInfo = meta.sortInfo || this.sortInfo;
15752         this.modified = [];
15753         this.fireEvent('metachange', this, this.reader.meta);
15754     },
15755     
15756     moveIndex : function(data, type)
15757     {
15758         var index = this.indexOf(data);
15759         
15760         var newIndex = index + type;
15761         
15762         this.remove(data);
15763         
15764         this.insert(newIndex, data);
15765         
15766     }
15767 });/*
15768  * Based on:
15769  * Ext JS Library 1.1.1
15770  * Copyright(c) 2006-2007, Ext JS, LLC.
15771  *
15772  * Originally Released Under LGPL - original licence link has changed is not relivant.
15773  *
15774  * Fork - LGPL
15775  * <script type="text/javascript">
15776  */
15777
15778 /**
15779  * @class Roo.data.SimpleStore
15780  * @extends Roo.data.Store
15781  * Small helper class to make creating Stores from Array data easier.
15782  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15783  * @cfg {Array} fields An array of field definition objects, or field name strings.
15784  * @cfg {Object} an existing reader (eg. copied from another store)
15785  * @cfg {Array} data The multi-dimensional array of data
15786  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15787  * @cfg {Roo.data.Reader} reader  [not-required] 
15788  * @constructor
15789  * @param {Object} config
15790  */
15791 Roo.data.SimpleStore = function(config)
15792 {
15793     Roo.data.SimpleStore.superclass.constructor.call(this, {
15794         isLocal : true,
15795         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15796                 id: config.id
15797             },
15798             Roo.data.Record.create(config.fields)
15799         ),
15800         proxy : new Roo.data.MemoryProxy(config.data)
15801     });
15802     this.load();
15803 };
15804 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15805  * Based on:
15806  * Ext JS Library 1.1.1
15807  * Copyright(c) 2006-2007, Ext JS, LLC.
15808  *
15809  * Originally Released Under LGPL - original licence link has changed is not relivant.
15810  *
15811  * Fork - LGPL
15812  * <script type="text/javascript">
15813  */
15814
15815 /**
15816 /**
15817  * @extends Roo.data.Store
15818  * @class Roo.data.JsonStore
15819  * Small helper class to make creating Stores for JSON data easier. <br/>
15820 <pre><code>
15821 var store = new Roo.data.JsonStore({
15822     url: 'get-images.php',
15823     root: 'images',
15824     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15825 });
15826 </code></pre>
15827  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15828  * JsonReader and HttpProxy (unless inline data is provided).</b>
15829  * @cfg {Array} fields An array of field definition objects, or field name strings.
15830  * @constructor
15831  * @param {Object} config
15832  */
15833 Roo.data.JsonStore = function(c){
15834     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15835         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15836         reader: new Roo.data.JsonReader(c, c.fields)
15837     }));
15838 };
15839 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15840  * Based on:
15841  * Ext JS Library 1.1.1
15842  * Copyright(c) 2006-2007, Ext JS, LLC.
15843  *
15844  * Originally Released Under LGPL - original licence link has changed is not relivant.
15845  *
15846  * Fork - LGPL
15847  * <script type="text/javascript">
15848  */
15849
15850  
15851 Roo.data.Field = function(config){
15852     if(typeof config == "string"){
15853         config = {name: config};
15854     }
15855     Roo.apply(this, config);
15856     
15857     if(!this.type){
15858         this.type = "auto";
15859     }
15860     
15861     var st = Roo.data.SortTypes;
15862     // named sortTypes are supported, here we look them up
15863     if(typeof this.sortType == "string"){
15864         this.sortType = st[this.sortType];
15865     }
15866     
15867     // set default sortType for strings and dates
15868     if(!this.sortType){
15869         switch(this.type){
15870             case "string":
15871                 this.sortType = st.asUCString;
15872                 break;
15873             case "date":
15874                 this.sortType = st.asDate;
15875                 break;
15876             default:
15877                 this.sortType = st.none;
15878         }
15879     }
15880
15881     // define once
15882     var stripRe = /[\$,%]/g;
15883
15884     // prebuilt conversion function for this field, instead of
15885     // switching every time we're reading a value
15886     if(!this.convert){
15887         var cv, dateFormat = this.dateFormat;
15888         switch(this.type){
15889             case "":
15890             case "auto":
15891             case undefined:
15892                 cv = function(v){ return v; };
15893                 break;
15894             case "string":
15895                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15896                 break;
15897             case "int":
15898                 cv = function(v){
15899                     return v !== undefined && v !== null && v !== '' ?
15900                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15901                     };
15902                 break;
15903             case "float":
15904                 cv = function(v){
15905                     return v !== undefined && v !== null && v !== '' ?
15906                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15907                     };
15908                 break;
15909             case "bool":
15910             case "boolean":
15911                 cv = function(v){ return v === true || v === "true" || v == 1; };
15912                 break;
15913             case "date":
15914                 cv = function(v){
15915                     if(!v){
15916                         return '';
15917                     }
15918                     if(v instanceof Date){
15919                         return v;
15920                     }
15921                     if(dateFormat){
15922                         if(dateFormat == "timestamp"){
15923                             return new Date(v*1000);
15924                         }
15925                         return Date.parseDate(v, dateFormat);
15926                     }
15927                     var parsed = Date.parse(v);
15928                     return parsed ? new Date(parsed) : null;
15929                 };
15930              break;
15931             
15932         }
15933         this.convert = cv;
15934     }
15935 };
15936
15937 Roo.data.Field.prototype = {
15938     dateFormat: null,
15939     defaultValue: "",
15940     mapping: null,
15941     sortType : null,
15942     sortDir : "ASC"
15943 };/*
15944  * Based on:
15945  * Ext JS Library 1.1.1
15946  * Copyright(c) 2006-2007, Ext JS, LLC.
15947  *
15948  * Originally Released Under LGPL - original licence link has changed is not relivant.
15949  *
15950  * Fork - LGPL
15951  * <script type="text/javascript">
15952  */
15953  
15954 // Base class for reading structured data from a data source.  This class is intended to be
15955 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15956
15957 /**
15958  * @class Roo.data.DataReader
15959  * @abstract
15960  * Base class for reading structured data from a data source.  This class is intended to be
15961  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15962  */
15963
15964 Roo.data.DataReader = function(meta, recordType){
15965     
15966     this.meta = meta;
15967     
15968     this.recordType = recordType instanceof Array ? 
15969         Roo.data.Record.create(recordType) : recordType;
15970 };
15971
15972 Roo.data.DataReader.prototype = {
15973     
15974     
15975     readerType : 'Data',
15976      /**
15977      * Create an empty record
15978      * @param {Object} data (optional) - overlay some values
15979      * @return {Roo.data.Record} record created.
15980      */
15981     newRow :  function(d) {
15982         var da =  {};
15983         this.recordType.prototype.fields.each(function(c) {
15984             switch( c.type) {
15985                 case 'int' : da[c.name] = 0; break;
15986                 case 'date' : da[c.name] = new Date(); break;
15987                 case 'float' : da[c.name] = 0.0; break;
15988                 case 'boolean' : da[c.name] = false; break;
15989                 default : da[c.name] = ""; break;
15990             }
15991             
15992         });
15993         return new this.recordType(Roo.apply(da, d));
15994     }
15995     
15996     
15997 };/*
15998  * Based on:
15999  * Ext JS Library 1.1.1
16000  * Copyright(c) 2006-2007, Ext JS, LLC.
16001  *
16002  * Originally Released Under LGPL - original licence link has changed is not relivant.
16003  *
16004  * Fork - LGPL
16005  * <script type="text/javascript">
16006  */
16007
16008 /**
16009  * @class Roo.data.DataProxy
16010  * @extends Roo.util.Observable
16011  * @abstract
16012  * This class is an abstract base class for implementations which provide retrieval of
16013  * unformatted data objects.<br>
16014  * <p>
16015  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16016  * (of the appropriate type which knows how to parse the data object) to provide a block of
16017  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16018  * <p>
16019  * Custom implementations must implement the load method as described in
16020  * {@link Roo.data.HttpProxy#load}.
16021  */
16022 Roo.data.DataProxy = function(){
16023     this.addEvents({
16024         /**
16025          * @event beforeload
16026          * Fires before a network request is made to retrieve a data object.
16027          * @param {Object} This DataProxy object.
16028          * @param {Object} params The params parameter to the load function.
16029          */
16030         beforeload : true,
16031         /**
16032          * @event load
16033          * Fires before the load method's callback is called.
16034          * @param {Object} This DataProxy object.
16035          * @param {Object} o The data object.
16036          * @param {Object} arg The callback argument object passed to the load function.
16037          */
16038         load : true,
16039         /**
16040          * @event loadexception
16041          * Fires if an Exception occurs during data retrieval.
16042          * @param {Object} This DataProxy object.
16043          * @param {Object} o The data object.
16044          * @param {Object} arg The callback argument object passed to the load function.
16045          * @param {Object} e The Exception.
16046          */
16047         loadexception : true
16048     });
16049     Roo.data.DataProxy.superclass.constructor.call(this);
16050 };
16051
16052 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16053
16054     /**
16055      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16056      */
16057 /*
16058  * Based on:
16059  * Ext JS Library 1.1.1
16060  * Copyright(c) 2006-2007, Ext JS, LLC.
16061  *
16062  * Originally Released Under LGPL - original licence link has changed is not relivant.
16063  *
16064  * Fork - LGPL
16065  * <script type="text/javascript">
16066  */
16067 /**
16068  * @class Roo.data.MemoryProxy
16069  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16070  * to the Reader when its load method is called.
16071  * @constructor
16072  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16073  */
16074 Roo.data.MemoryProxy = function(data){
16075     if (data.data) {
16076         data = data.data;
16077     }
16078     Roo.data.MemoryProxy.superclass.constructor.call(this);
16079     this.data = data;
16080 };
16081
16082 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16083     
16084     /**
16085      * Load data from the requested source (in this case an in-memory
16086      * data object passed to the constructor), read the data object into
16087      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16088      * process that block using the passed callback.
16089      * @param {Object} params This parameter is not used by the MemoryProxy class.
16090      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16091      * object into a block of Roo.data.Records.
16092      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16093      * The function must be passed <ul>
16094      * <li>The Record block object</li>
16095      * <li>The "arg" argument from the load function</li>
16096      * <li>A boolean success indicator</li>
16097      * </ul>
16098      * @param {Object} scope The scope in which to call the callback
16099      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16100      */
16101     load : function(params, reader, callback, scope, arg){
16102         params = params || {};
16103         var result;
16104         try {
16105             result = reader.readRecords(params.data ? params.data :this.data);
16106         }catch(e){
16107             this.fireEvent("loadexception", this, arg, null, e);
16108             callback.call(scope, null, arg, false);
16109             return;
16110         }
16111         callback.call(scope, result, arg, true);
16112     },
16113     
16114     // private
16115     update : function(params, records){
16116         
16117     }
16118 });/*
16119  * Based on:
16120  * Ext JS Library 1.1.1
16121  * Copyright(c) 2006-2007, Ext JS, LLC.
16122  *
16123  * Originally Released Under LGPL - original licence link has changed is not relivant.
16124  *
16125  * Fork - LGPL
16126  * <script type="text/javascript">
16127  */
16128 /**
16129  * @class Roo.data.HttpProxy
16130  * @extends Roo.data.DataProxy
16131  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16132  * configured to reference a certain URL.<br><br>
16133  * <p>
16134  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16135  * from which the running page was served.<br><br>
16136  * <p>
16137  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16138  * <p>
16139  * Be aware that to enable the browser to parse an XML document, the server must set
16140  * the Content-Type header in the HTTP response to "text/xml".
16141  * @constructor
16142  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16143  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16144  * will be used to make the request.
16145  */
16146 Roo.data.HttpProxy = function(conn){
16147     Roo.data.HttpProxy.superclass.constructor.call(this);
16148     // is conn a conn config or a real conn?
16149     this.conn = conn;
16150     this.useAjax = !conn || !conn.events;
16151   
16152 };
16153
16154 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16155     // thse are take from connection...
16156     
16157     /**
16158      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16159      */
16160     /**
16161      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16162      * extra parameters to each request made by this object. (defaults to undefined)
16163      */
16164     /**
16165      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16166      *  to each request made by this object. (defaults to undefined)
16167      */
16168     /**
16169      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16170      */
16171     /**
16172      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16173      */
16174      /**
16175      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16176      * @type Boolean
16177      */
16178   
16179
16180     /**
16181      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16182      * @type Boolean
16183      */
16184     /**
16185      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16186      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16187      * a finer-grained basis than the DataProxy events.
16188      */
16189     getConnection : function(){
16190         return this.useAjax ? Roo.Ajax : this.conn;
16191     },
16192
16193     /**
16194      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16195      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16196      * process that block using the passed callback.
16197      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16198      * for the request to the remote server.
16199      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16200      * object into a block of Roo.data.Records.
16201      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16202      * The function must be passed <ul>
16203      * <li>The Record block object</li>
16204      * <li>The "arg" argument from the load function</li>
16205      * <li>A boolean success indicator</li>
16206      * </ul>
16207      * @param {Object} scope The scope in which to call the callback
16208      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16209      */
16210     load : function(params, reader, callback, scope, arg){
16211         if(this.fireEvent("beforeload", this, params) !== false){
16212             var  o = {
16213                 params : params || {},
16214                 request: {
16215                     callback : callback,
16216                     scope : scope,
16217                     arg : arg
16218                 },
16219                 reader: reader,
16220                 callback : this.loadResponse,
16221                 scope: this
16222             };
16223             if(this.useAjax){
16224                 Roo.applyIf(o, this.conn);
16225                 if(this.activeRequest){
16226                     Roo.Ajax.abort(this.activeRequest);
16227                 }
16228                 this.activeRequest = Roo.Ajax.request(o);
16229             }else{
16230                 this.conn.request(o);
16231             }
16232         }else{
16233             callback.call(scope||this, null, arg, false);
16234         }
16235     },
16236
16237     // private
16238     loadResponse : function(o, success, response){
16239         delete this.activeRequest;
16240         if(!success){
16241             this.fireEvent("loadexception", this, o, response);
16242             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16243             return;
16244         }
16245         var result;
16246         try {
16247             result = o.reader.read(response);
16248         }catch(e){
16249             o.success = false;
16250             o.raw = { errorMsg : response.responseText };
16251             this.fireEvent("loadexception", this, o, response, e);
16252             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16253             return;
16254         }
16255         
16256         this.fireEvent("load", this, o, o.request.arg);
16257         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16258     },
16259
16260     // private
16261     update : function(dataSet){
16262
16263     },
16264
16265     // private
16266     updateResponse : function(dataSet){
16267
16268     }
16269 });/*
16270  * Based on:
16271  * Ext JS Library 1.1.1
16272  * Copyright(c) 2006-2007, Ext JS, LLC.
16273  *
16274  * Originally Released Under LGPL - original licence link has changed is not relivant.
16275  *
16276  * Fork - LGPL
16277  * <script type="text/javascript">
16278  */
16279
16280 /**
16281  * @class Roo.data.ScriptTagProxy
16282  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16283  * other than the originating domain of the running page.<br><br>
16284  * <p>
16285  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
16286  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16287  * <p>
16288  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16289  * source code that is used as the source inside a &lt;script> tag.<br><br>
16290  * <p>
16291  * In order for the browser to process the returned data, the server must wrap the data object
16292  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16293  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16294  * depending on whether the callback name was passed:
16295  * <p>
16296  * <pre><code>
16297 boolean scriptTag = false;
16298 String cb = request.getParameter("callback");
16299 if (cb != null) {
16300     scriptTag = true;
16301     response.setContentType("text/javascript");
16302 } else {
16303     response.setContentType("application/x-json");
16304 }
16305 Writer out = response.getWriter();
16306 if (scriptTag) {
16307     out.write(cb + "(");
16308 }
16309 out.print(dataBlock.toJsonString());
16310 if (scriptTag) {
16311     out.write(");");
16312 }
16313 </pre></code>
16314  *
16315  * @constructor
16316  * @param {Object} config A configuration object.
16317  */
16318 Roo.data.ScriptTagProxy = function(config){
16319     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16320     Roo.apply(this, config);
16321     this.head = document.getElementsByTagName("head")[0];
16322 };
16323
16324 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16325
16326 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16327     /**
16328      * @cfg {String} url The URL from which to request the data object.
16329      */
16330     /**
16331      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16332      */
16333     timeout : 30000,
16334     /**
16335      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16336      * the server the name of the callback function set up by the load call to process the returned data object.
16337      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16338      * javascript output which calls this named function passing the data object as its only parameter.
16339      */
16340     callbackParam : "callback",
16341     /**
16342      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16343      * name to the request.
16344      */
16345     nocache : true,
16346
16347     /**
16348      * Load data from the configured URL, read the data object into
16349      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16350      * process that block using the passed callback.
16351      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16352      * for the request to the remote server.
16353      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16354      * object into a block of Roo.data.Records.
16355      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16356      * The function must be passed <ul>
16357      * <li>The Record block object</li>
16358      * <li>The "arg" argument from the load function</li>
16359      * <li>A boolean success indicator</li>
16360      * </ul>
16361      * @param {Object} scope The scope in which to call the callback
16362      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16363      */
16364     load : function(params, reader, callback, scope, arg){
16365         if(this.fireEvent("beforeload", this, params) !== false){
16366
16367             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16368
16369             var url = this.url;
16370             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16371             if(this.nocache){
16372                 url += "&_dc=" + (new Date().getTime());
16373             }
16374             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16375             var trans = {
16376                 id : transId,
16377                 cb : "stcCallback"+transId,
16378                 scriptId : "stcScript"+transId,
16379                 params : params,
16380                 arg : arg,
16381                 url : url,
16382                 callback : callback,
16383                 scope : scope,
16384                 reader : reader
16385             };
16386             var conn = this;
16387
16388             window[trans.cb] = function(o){
16389                 conn.handleResponse(o, trans);
16390             };
16391
16392             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16393
16394             if(this.autoAbort !== false){
16395                 this.abort();
16396             }
16397
16398             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16399
16400             var script = document.createElement("script");
16401             script.setAttribute("src", url);
16402             script.setAttribute("type", "text/javascript");
16403             script.setAttribute("id", trans.scriptId);
16404             this.head.appendChild(script);
16405
16406             this.trans = trans;
16407         }else{
16408             callback.call(scope||this, null, arg, false);
16409         }
16410     },
16411
16412     // private
16413     isLoading : function(){
16414         return this.trans ? true : false;
16415     },
16416
16417     /**
16418      * Abort the current server request.
16419      */
16420     abort : function(){
16421         if(this.isLoading()){
16422             this.destroyTrans(this.trans);
16423         }
16424     },
16425
16426     // private
16427     destroyTrans : function(trans, isLoaded){
16428         this.head.removeChild(document.getElementById(trans.scriptId));
16429         clearTimeout(trans.timeoutId);
16430         if(isLoaded){
16431             window[trans.cb] = undefined;
16432             try{
16433                 delete window[trans.cb];
16434             }catch(e){}
16435         }else{
16436             // if hasn't been loaded, wait for load to remove it to prevent script error
16437             window[trans.cb] = function(){
16438                 window[trans.cb] = undefined;
16439                 try{
16440                     delete window[trans.cb];
16441                 }catch(e){}
16442             };
16443         }
16444     },
16445
16446     // private
16447     handleResponse : function(o, trans){
16448         this.trans = false;
16449         this.destroyTrans(trans, true);
16450         var result;
16451         try {
16452             result = trans.reader.readRecords(o);
16453         }catch(e){
16454             this.fireEvent("loadexception", this, o, trans.arg, e);
16455             trans.callback.call(trans.scope||window, null, trans.arg, false);
16456             return;
16457         }
16458         this.fireEvent("load", this, o, trans.arg);
16459         trans.callback.call(trans.scope||window, result, trans.arg, true);
16460     },
16461
16462     // private
16463     handleFailure : function(trans){
16464         this.trans = false;
16465         this.destroyTrans(trans, false);
16466         this.fireEvent("loadexception", this, null, trans.arg);
16467         trans.callback.call(trans.scope||window, null, trans.arg, false);
16468     }
16469 });/*
16470  * Based on:
16471  * Ext JS Library 1.1.1
16472  * Copyright(c) 2006-2007, Ext JS, LLC.
16473  *
16474  * Originally Released Under LGPL - original licence link has changed is not relivant.
16475  *
16476  * Fork - LGPL
16477  * <script type="text/javascript">
16478  */
16479
16480 /**
16481  * @class Roo.data.JsonReader
16482  * @extends Roo.data.DataReader
16483  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16484  * based on mappings in a provided Roo.data.Record constructor.
16485  * 
16486  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16487  * in the reply previously. 
16488  * 
16489  * <p>
16490  * Example code:
16491  * <pre><code>
16492 var RecordDef = Roo.data.Record.create([
16493     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16494     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16495 ]);
16496 var myReader = new Roo.data.JsonReader({
16497     totalProperty: "results",    // The property which contains the total dataset size (optional)
16498     root: "rows",                // The property which contains an Array of row objects
16499     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16500 }, RecordDef);
16501 </code></pre>
16502  * <p>
16503  * This would consume a JSON file like this:
16504  * <pre><code>
16505 { 'results': 2, 'rows': [
16506     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16507     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16508 }
16509 </code></pre>
16510  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16511  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16512  * paged from the remote server.
16513  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16514  * @cfg {String} root name of the property which contains the Array of row objects.
16515  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16516  * @cfg {Array} fields Array of field definition objects
16517  * @constructor
16518  * Create a new JsonReader
16519  * @param {Object} meta Metadata configuration options
16520  * @param {Object} recordType Either an Array of field definition objects,
16521  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16522  */
16523 Roo.data.JsonReader = function(meta, recordType){
16524     
16525     meta = meta || {};
16526     // set some defaults:
16527     Roo.applyIf(meta, {
16528         totalProperty: 'total',
16529         successProperty : 'success',
16530         root : 'data',
16531         id : 'id'
16532     });
16533     
16534     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16535 };
16536 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16537     
16538     readerType : 'Json',
16539     
16540     /**
16541      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16542      * Used by Store query builder to append _requestMeta to params.
16543      * 
16544      */
16545     metaFromRemote : false,
16546     /**
16547      * This method is only used by a DataProxy which has retrieved data from a remote server.
16548      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16549      * @return {Object} data A data block which is used by an Roo.data.Store object as
16550      * a cache of Roo.data.Records.
16551      */
16552     read : function(response){
16553         var json = response.responseText;
16554        
16555         var o = /* eval:var:o */ eval("("+json+")");
16556         if(!o) {
16557             throw {message: "JsonReader.read: Json object not found"};
16558         }
16559         
16560         if(o.metaData){
16561             
16562             delete this.ef;
16563             this.metaFromRemote = true;
16564             this.meta = o.metaData;
16565             this.recordType = Roo.data.Record.create(o.metaData.fields);
16566             this.onMetaChange(this.meta, this.recordType, o);
16567         }
16568         return this.readRecords(o);
16569     },
16570
16571     // private function a store will implement
16572     onMetaChange : function(meta, recordType, o){
16573
16574     },
16575
16576     /**
16577          * @ignore
16578          */
16579     simpleAccess: function(obj, subsc) {
16580         return obj[subsc];
16581     },
16582
16583         /**
16584          * @ignore
16585          */
16586     getJsonAccessor: function(){
16587         var re = /[\[\.]/;
16588         return function(expr) {
16589             try {
16590                 return(re.test(expr))
16591                     ? new Function("obj", "return obj." + expr)
16592                     : function(obj){
16593                         return obj[expr];
16594                     };
16595             } catch(e){}
16596             return Roo.emptyFn;
16597         };
16598     }(),
16599
16600     /**
16601      * Create a data block containing Roo.data.Records from an XML document.
16602      * @param {Object} o An object which contains an Array of row objects in the property specified
16603      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16604      * which contains the total size of the dataset.
16605      * @return {Object} data A data block which is used by an Roo.data.Store object as
16606      * a cache of Roo.data.Records.
16607      */
16608     readRecords : function(o){
16609         /**
16610          * After any data loads, the raw JSON data is available for further custom processing.
16611          * @type Object
16612          */
16613         this.o = o;
16614         var s = this.meta, Record = this.recordType,
16615             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16616
16617 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16618         if (!this.ef) {
16619             if(s.totalProperty) {
16620                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16621                 }
16622                 if(s.successProperty) {
16623                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16624                 }
16625                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16626                 if (s.id) {
16627                         var g = this.getJsonAccessor(s.id);
16628                         this.getId = function(rec) {
16629                                 var r = g(rec);  
16630                                 return (r === undefined || r === "") ? null : r;
16631                         };
16632                 } else {
16633                         this.getId = function(){return null;};
16634                 }
16635             this.ef = [];
16636             for(var jj = 0; jj < fl; jj++){
16637                 f = fi[jj];
16638                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16639                 this.ef[jj] = this.getJsonAccessor(map);
16640             }
16641         }
16642
16643         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16644         if(s.totalProperty){
16645             var vt = parseInt(this.getTotal(o), 10);
16646             if(!isNaN(vt)){
16647                 totalRecords = vt;
16648             }
16649         }
16650         if(s.successProperty){
16651             var vs = this.getSuccess(o);
16652             if(vs === false || vs === 'false'){
16653                 success = false;
16654             }
16655         }
16656         var records = [];
16657         for(var i = 0; i < c; i++){
16658                 var n = root[i];
16659             var values = {};
16660             var id = this.getId(n);
16661             for(var j = 0; j < fl; j++){
16662                 f = fi[j];
16663             var v = this.ef[j](n);
16664             if (!f.convert) {
16665                 Roo.log('missing convert for ' + f.name);
16666                 Roo.log(f);
16667                 continue;
16668             }
16669             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16670             }
16671             var record = new Record(values, id);
16672             record.json = n;
16673             records[i] = record;
16674         }
16675         return {
16676             raw : o,
16677             success : success,
16678             records : records,
16679             totalRecords : totalRecords
16680         };
16681     },
16682     // used when loading children.. @see loadDataFromChildren
16683     toLoadData: function(rec)
16684     {
16685         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16686         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16687         return { data : data, total : data.length };
16688         
16689     }
16690 });/*
16691  * Based on:
16692  * Ext JS Library 1.1.1
16693  * Copyright(c) 2006-2007, Ext JS, LLC.
16694  *
16695  * Originally Released Under LGPL - original licence link has changed is not relivant.
16696  *
16697  * Fork - LGPL
16698  * <script type="text/javascript">
16699  */
16700
16701 /**
16702  * @class Roo.data.ArrayReader
16703  * @extends Roo.data.DataReader
16704  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16705  * Each element of that Array represents a row of data fields. The
16706  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16707  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16708  * <p>
16709  * Example code:.
16710  * <pre><code>
16711 var RecordDef = Roo.data.Record.create([
16712     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16713     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16714 ]);
16715 var myReader = new Roo.data.ArrayReader({
16716     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16717 }, RecordDef);
16718 </code></pre>
16719  * <p>
16720  * This would consume an Array like this:
16721  * <pre><code>
16722 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16723   </code></pre>
16724  
16725  * @constructor
16726  * Create a new JsonReader
16727  * @param {Object} meta Metadata configuration options.
16728  * @param {Object|Array} recordType Either an Array of field definition objects
16729  * 
16730  * @cfg {Array} fields Array of field definition objects
16731  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16732  * as specified to {@link Roo.data.Record#create},
16733  * or an {@link Roo.data.Record} object
16734  *
16735  * 
16736  * created using {@link Roo.data.Record#create}.
16737  */
16738 Roo.data.ArrayReader = function(meta, recordType)
16739 {    
16740     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16741 };
16742
16743 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16744     
16745       /**
16746      * Create a data block containing Roo.data.Records from an XML document.
16747      * @param {Object} o An Array of row objects which represents the dataset.
16748      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16749      * a cache of Roo.data.Records.
16750      */
16751     readRecords : function(o)
16752     {
16753         var sid = this.meta ? this.meta.id : null;
16754         var recordType = this.recordType, fields = recordType.prototype.fields;
16755         var records = [];
16756         var root = o;
16757         for(var i = 0; i < root.length; i++){
16758             var n = root[i];
16759             var values = {};
16760             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16761             for(var j = 0, jlen = fields.length; j < jlen; j++){
16762                 var f = fields.items[j];
16763                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16764                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16765                 v = f.convert(v);
16766                 values[f.name] = v;
16767             }
16768             var record = new recordType(values, id);
16769             record.json = n;
16770             records[records.length] = record;
16771         }
16772         return {
16773             records : records,
16774             totalRecords : records.length
16775         };
16776     },
16777     // used when loading children.. @see loadDataFromChildren
16778     toLoadData: function(rec)
16779     {
16780         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16781         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16782         
16783     }
16784     
16785     
16786 });/*
16787  * - LGPL
16788  * * 
16789  */
16790
16791 /**
16792  * @class Roo.bootstrap.form.ComboBox
16793  * @extends Roo.bootstrap.form.TriggerField
16794  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16795  * @cfg {Boolean} append (true|false) default false
16796  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16797  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16798  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16799  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16800  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16801  * @cfg {Boolean} animate default true
16802  * @cfg {Boolean} emptyResultText only for touch device
16803  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16804  * @cfg {String} emptyTitle default ''
16805  * @cfg {Number} width fixed with? experimental
16806  * @constructor
16807  * Create a new ComboBox.
16808  * @param {Object} config Configuration options
16809  */
16810 Roo.bootstrap.form.ComboBox = function(config){
16811     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16812     this.addEvents({
16813         /**
16814          * @event expand
16815          * Fires when the dropdown list is expanded
16816         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16817         */
16818         'expand' : true,
16819         /**
16820          * @event collapse
16821          * Fires when the dropdown list is collapsed
16822         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16823         */
16824         'collapse' : true,
16825         /**
16826          * @event beforeselect
16827          * Fires before a list item is selected. Return false to cancel the selection.
16828         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16829         * @param {Roo.data.Record} record The data record returned from the underlying store
16830         * @param {Number} index The index of the selected item in the dropdown list
16831         */
16832         'beforeselect' : true,
16833         /**
16834          * @event select
16835          * Fires when a list item is selected
16836         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16837         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16838         * @param {Number} index The index of the selected item in the dropdown list
16839         */
16840         'select' : true,
16841         /**
16842          * @event beforequery
16843          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16844          * The event object passed has these properties:
16845         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16846         * @param {String} query The query
16847         * @param {Boolean} forceAll true to force "all" query
16848         * @param {Boolean} cancel true to cancel the query
16849         * @param {Object} e The query event object
16850         */
16851         'beforequery': true,
16852          /**
16853          * @event add
16854          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16855         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16856         */
16857         'add' : true,
16858         /**
16859          * @event edit
16860          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16861         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16862         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16863         */
16864         'edit' : true,
16865         /**
16866          * @event remove
16867          * Fires when the remove value from the combobox array
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         */
16870         'remove' : true,
16871         /**
16872          * @event afterremove
16873          * Fires when the remove value from the combobox array
16874         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16875         */
16876         'afterremove' : true,
16877         /**
16878          * @event specialfilter
16879          * Fires when specialfilter
16880             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16881             */
16882         'specialfilter' : true,
16883         /**
16884          * @event tick
16885          * Fires when tick the element
16886             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16887             */
16888         'tick' : true,
16889         /**
16890          * @event touchviewdisplay
16891          * Fires when touch view require special display (default is using displayField)
16892             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16893             * @param {Object} cfg set html .
16894             */
16895         'touchviewdisplay' : true
16896         
16897     });
16898     
16899     this.item = [];
16900     this.tickItems = [];
16901     
16902     this.selectedIndex = -1;
16903     if(this.mode == 'local'){
16904         if(config.queryDelay === undefined){
16905             this.queryDelay = 10;
16906         }
16907         if(config.minChars === undefined){
16908             this.minChars = 0;
16909         }
16910     }
16911 };
16912
16913 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16914      
16915     /**
16916      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16917      * rendering into an Roo.Editor, defaults to false)
16918      */
16919     /**
16920      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16921      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16922      */
16923     /**
16924      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16925      */
16926     /**
16927      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16928      * the dropdown list (defaults to undefined, with no header element)
16929      */
16930
16931      /**
16932      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16933      */
16934      
16935      /**
16936      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16937      */
16938     listWidth: undefined,
16939     /**
16940      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16941      * mode = 'remote' or 'text' if mode = 'local')
16942      */
16943     displayField: undefined,
16944     
16945     /**
16946      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16947      * mode = 'remote' or 'value' if mode = 'local'). 
16948      * Note: use of a valueField requires the user make a selection
16949      * in order for a value to be mapped.
16950      */
16951     valueField: undefined,
16952     /**
16953      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16954      */
16955     modalTitle : '',
16956     
16957     /**
16958      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16959      * field's data value (defaults to the underlying DOM element's name)
16960      */
16961     hiddenName: undefined,
16962     /**
16963      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16964      */
16965     listClass: '',
16966     /**
16967      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16968      */
16969     selectedClass: 'active',
16970     
16971     /**
16972      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16973      */
16974     shadow:'sides',
16975     /**
16976      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16977      * anchor positions (defaults to 'tl-bl')
16978      */
16979     listAlign: 'tl-bl?',
16980     /**
16981      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16982      */
16983     maxHeight: 300,
16984     /**
16985      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16986      * query specified by the allQuery config option (defaults to 'query')
16987      */
16988     triggerAction: 'query',
16989     /**
16990      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16991      * (defaults to 4, does not apply if editable = false)
16992      */
16993     minChars : 4,
16994     /**
16995      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16996      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16997      */
16998     typeAhead: false,
16999     /**
17000      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17001      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17002      */
17003     queryDelay: 500,
17004     /**
17005      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17006      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17007      */
17008     pageSize: 0,
17009     /**
17010      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17011      * when editable = true (defaults to false)
17012      */
17013     selectOnFocus:false,
17014     /**
17015      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17016      */
17017     queryParam: 'query',
17018     /**
17019      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17020      * when mode = 'remote' (defaults to 'Loading...')
17021      */
17022     loadingText: 'Loading...',
17023     /**
17024      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17025      */
17026     resizable: false,
17027     /**
17028      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17029      */
17030     handleHeight : 8,
17031     /**
17032      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17033      * traditional select (defaults to true)
17034      */
17035     editable: true,
17036     /**
17037      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17038      */
17039     allQuery: '',
17040     /**
17041      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17042      */
17043     mode: 'remote',
17044     /**
17045      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17046      * listWidth has a higher value)
17047      */
17048     minListWidth : 70,
17049     /**
17050      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17051      * allow the user to set arbitrary text into the field (defaults to false)
17052      */
17053     forceSelection:false,
17054     /**
17055      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17056      * if typeAhead = true (defaults to 250)
17057      */
17058     typeAheadDelay : 250,
17059     /**
17060      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17061      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17062      */
17063     valueNotFoundText : undefined,
17064     /**
17065      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17066      */
17067     blockFocus : false,
17068     
17069     /**
17070      * @cfg {Boolean} disableClear Disable showing of clear button.
17071      */
17072     disableClear : false,
17073     /**
17074      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17075      */
17076     alwaysQuery : false,
17077     
17078     /**
17079      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17080      */
17081     multiple : false,
17082     
17083     /**
17084      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17085      */
17086     invalidClass : "has-warning",
17087     
17088     /**
17089      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17090      */
17091     validClass : "has-success",
17092     
17093     /**
17094      * @cfg {Boolean} specialFilter (true|false) special filter default false
17095      */
17096     specialFilter : false,
17097     
17098     /**
17099      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17100      */
17101     mobileTouchView : true,
17102     
17103     /**
17104      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17105      */
17106     useNativeIOS : false,
17107     
17108     /**
17109      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17110      */
17111     mobile_restrict_height : false,
17112     
17113     ios_options : false,
17114     
17115     //private
17116     addicon : false,
17117     editicon: false,
17118     
17119     page: 0,
17120     hasQuery: false,
17121     append: false,
17122     loadNext: false,
17123     autoFocus : true,
17124     tickable : false,
17125     btnPosition : 'right',
17126     triggerList : true,
17127     showToggleBtn : true,
17128     animate : true,
17129     emptyResultText: 'Empty',
17130     triggerText : 'Select',
17131     emptyTitle : '',
17132     width : false,
17133     
17134     // element that contains real text value.. (when hidden is used..)
17135     
17136     getAutoCreate : function()
17137     {   
17138         var cfg = false;
17139         //render
17140         /*
17141          * Render classic select for iso
17142          */
17143         
17144         if(Roo.isIOS && this.useNativeIOS){
17145             cfg = this.getAutoCreateNativeIOS();
17146             return cfg;
17147         }
17148         
17149         /*
17150          * Touch Devices
17151          */
17152         
17153         if(Roo.isTouch && this.mobileTouchView){
17154             cfg = this.getAutoCreateTouchView();
17155             return cfg;;
17156         }
17157         
17158         /*
17159          *  Normal ComboBox
17160          */
17161         if(!this.tickable){
17162             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17163             return cfg;
17164         }
17165         
17166         /*
17167          *  ComboBox with tickable selections
17168          */
17169              
17170         var align = this.labelAlign || this.parentLabelAlign();
17171         
17172         cfg = {
17173             cls : 'form-group roo-combobox-tickable' //input-group
17174         };
17175         
17176         var btn_text_select = '';
17177         var btn_text_done = '';
17178         var btn_text_cancel = '';
17179         
17180         if (this.btn_text_show) {
17181             btn_text_select = 'Select';
17182             btn_text_done = 'Done';
17183             btn_text_cancel = 'Cancel'; 
17184         }
17185         
17186         var buttons = {
17187             tag : 'div',
17188             cls : 'tickable-buttons',
17189             cn : [
17190                 {
17191                     tag : 'button',
17192                     type : 'button',
17193                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17194                     //html : this.triggerText
17195                     html: btn_text_select
17196                 },
17197                 {
17198                     tag : 'button',
17199                     type : 'button',
17200                     name : 'ok',
17201                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17202                     //html : 'Done'
17203                     html: btn_text_done
17204                 },
17205                 {
17206                     tag : 'button',
17207                     type : 'button',
17208                     name : 'cancel',
17209                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17210                     //html : 'Cancel'
17211                     html: btn_text_cancel
17212                 }
17213             ]
17214         };
17215         
17216         if(this.editable){
17217             buttons.cn.unshift({
17218                 tag: 'input',
17219                 cls: 'roo-select2-search-field-input'
17220             });
17221         }
17222         
17223         var _this = this;
17224         
17225         Roo.each(buttons.cn, function(c){
17226             if (_this.size) {
17227                 c.cls += ' btn-' + _this.size;
17228             }
17229
17230             if (_this.disabled) {
17231                 c.disabled = true;
17232             }
17233         });
17234         
17235         var box = {
17236             tag: 'div',
17237             style : 'display: contents',
17238             cn: [
17239                 {
17240                     tag: 'input',
17241                     type : 'hidden',
17242                     cls: 'form-hidden-field'
17243                 },
17244                 {
17245                     tag: 'ul',
17246                     cls: 'roo-select2-choices',
17247                     cn:[
17248                         {
17249                             tag: 'li',
17250                             cls: 'roo-select2-search-field',
17251                             cn: [
17252                                 buttons
17253                             ]
17254                         }
17255                     ]
17256                 }
17257             ]
17258         };
17259         
17260         var combobox = {
17261             cls: 'roo-select2-container input-group roo-select2-container-multi',
17262             cn: [
17263                 
17264                 box
17265 //                {
17266 //                    tag: 'ul',
17267 //                    cls: 'typeahead typeahead-long dropdown-menu',
17268 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17269 //                }
17270             ]
17271         };
17272         
17273         if(this.hasFeedback && !this.allowBlank){
17274             
17275             var feedback = {
17276                 tag: 'span',
17277                 cls: 'glyphicon form-control-feedback'
17278             };
17279
17280             combobox.cn.push(feedback);
17281         }
17282         
17283         
17284         
17285         var indicator = {
17286             tag : 'i',
17287             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17288             tooltip : 'This field is required'
17289         };
17290         if (Roo.bootstrap.version == 4) {
17291             indicator = {
17292                 tag : 'i',
17293                 style : 'display:none'
17294             };
17295         }
17296         if (align ==='left' && this.fieldLabel.length) {
17297             
17298             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17299             
17300             cfg.cn = [
17301                 indicator,
17302                 {
17303                     tag: 'label',
17304                     'for' :  id,
17305                     cls : 'control-label col-form-label',
17306                     html : this.fieldLabel
17307
17308                 },
17309                 {
17310                     cls : "", 
17311                     cn: [
17312                         combobox
17313                     ]
17314                 }
17315
17316             ];
17317             
17318             var labelCfg = cfg.cn[1];
17319             var contentCfg = cfg.cn[2];
17320             
17321
17322             if(this.indicatorpos == 'right'){
17323                 
17324                 cfg.cn = [
17325                     {
17326                         tag: 'label',
17327                         'for' :  id,
17328                         cls : 'control-label col-form-label',
17329                         cn : [
17330                             {
17331                                 tag : 'span',
17332                                 html : this.fieldLabel
17333                             },
17334                             indicator
17335                         ]
17336                     },
17337                     {
17338                         cls : "",
17339                         cn: [
17340                             combobox
17341                         ]
17342                     }
17343
17344                 ];
17345                 
17346                 
17347                 
17348                 labelCfg = cfg.cn[0];
17349                 contentCfg = cfg.cn[1];
17350             
17351             }
17352             
17353             if(this.labelWidth > 12){
17354                 labelCfg.style = "width: " + this.labelWidth + 'px';
17355             }
17356             if(this.width * 1 > 0){
17357                 contentCfg.style = "width: " + this.width + 'px';
17358             }
17359             if(this.labelWidth < 13 && this.labelmd == 0){
17360                 this.labelmd = this.labelWidth;
17361             }
17362             
17363             if(this.labellg > 0){
17364                 labelCfg.cls += ' col-lg-' + this.labellg;
17365                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17366             }
17367             
17368             if(this.labelmd > 0){
17369                 labelCfg.cls += ' col-md-' + this.labelmd;
17370                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17371             }
17372             
17373             if(this.labelsm > 0){
17374                 labelCfg.cls += ' col-sm-' + this.labelsm;
17375                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17376             }
17377             
17378             if(this.labelxs > 0){
17379                 labelCfg.cls += ' col-xs-' + this.labelxs;
17380                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17381             }
17382                 
17383                 
17384         } else if ( this.fieldLabel.length) {
17385 //                Roo.log(" label");
17386                  cfg.cn = [
17387                    indicator,
17388                     {
17389                         tag: 'label',
17390                         //cls : 'input-group-addon',
17391                         html : this.fieldLabel
17392                     },
17393                     combobox
17394                 ];
17395                 
17396                 if(this.indicatorpos == 'right'){
17397                     cfg.cn = [
17398                         {
17399                             tag: 'label',
17400                             //cls : 'input-group-addon',
17401                             html : this.fieldLabel
17402                         },
17403                         indicator,
17404                         combobox
17405                     ];
17406                     
17407                 }
17408
17409         } else {
17410             
17411 //                Roo.log(" no label && no align");
17412                 cfg = combobox
17413                      
17414                 
17415         }
17416          
17417         var settings=this;
17418         ['xs','sm','md','lg'].map(function(size){
17419             if (settings[size]) {
17420                 cfg.cls += ' col-' + size + '-' + settings[size];
17421             }
17422         });
17423         
17424         return cfg;
17425         
17426     },
17427     
17428     _initEventsCalled : false,
17429     
17430     // private
17431     initEvents: function()
17432     {   
17433         if (this._initEventsCalled) { // as we call render... prevent looping...
17434             return;
17435         }
17436         this._initEventsCalled = true;
17437         
17438         if (!this.store) {
17439             throw "can not find store for combo";
17440         }
17441         
17442         this.indicator = this.indicatorEl();
17443         
17444         this.store = Roo.factory(this.store, Roo.data);
17445         this.store.parent = this;
17446         
17447         // if we are building from html. then this element is so complex, that we can not really
17448         // use the rendered HTML.
17449         // so we have to trash and replace the previous code.
17450         if (Roo.XComponent.build_from_html) {
17451             // remove this element....
17452             var e = this.el.dom, k=0;
17453             while (e ) { e = e.previousSibling;  ++k;}
17454
17455             this.el.remove();
17456             
17457             this.el=false;
17458             this.rendered = false;
17459             
17460             this.render(this.parent().getChildContainer(true), k);
17461         }
17462         
17463         if(Roo.isIOS && this.useNativeIOS){
17464             this.initIOSView();
17465             return;
17466         }
17467         
17468         /*
17469          * Touch Devices
17470          */
17471         
17472         if(Roo.isTouch && this.mobileTouchView){
17473             this.initTouchView();
17474             return;
17475         }
17476         
17477         if(this.tickable){
17478             this.initTickableEvents();
17479             return;
17480         }
17481         
17482         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17483         
17484         if(this.hiddenName){
17485             
17486             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17487             
17488             this.hiddenField.dom.value =
17489                 this.hiddenValue !== undefined ? this.hiddenValue :
17490                 this.value !== undefined ? this.value : '';
17491
17492             // prevent input submission
17493             this.el.dom.removeAttribute('name');
17494             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17495              
17496              
17497         }
17498         //if(Roo.isGecko){
17499         //    this.el.dom.setAttribute('autocomplete', 'off');
17500         //}
17501         
17502         var cls = 'x-combo-list';
17503         
17504         //this.list = new Roo.Layer({
17505         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17506         //});
17507         
17508         var _this = this;
17509         
17510         (function(){
17511             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17512             _this.list.setWidth(lw);
17513         }).defer(100);
17514         
17515         this.list.on('mouseover', this.onViewOver, this);
17516         this.list.on('mousemove', this.onViewMove, this);
17517         this.list.on('scroll', this.onViewScroll, this);
17518         
17519         /*
17520         this.list.swallowEvent('mousewheel');
17521         this.assetHeight = 0;
17522
17523         if(this.title){
17524             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17525             this.assetHeight += this.header.getHeight();
17526         }
17527
17528         this.innerList = this.list.createChild({cls:cls+'-inner'});
17529         this.innerList.on('mouseover', this.onViewOver, this);
17530         this.innerList.on('mousemove', this.onViewMove, this);
17531         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17532         
17533         if(this.allowBlank && !this.pageSize && !this.disableClear){
17534             this.footer = this.list.createChild({cls:cls+'-ft'});
17535             this.pageTb = new Roo.Toolbar(this.footer);
17536            
17537         }
17538         if(this.pageSize){
17539             this.footer = this.list.createChild({cls:cls+'-ft'});
17540             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17541                     {pageSize: this.pageSize});
17542             
17543         }
17544         
17545         if (this.pageTb && this.allowBlank && !this.disableClear) {
17546             var _this = this;
17547             this.pageTb.add(new Roo.Toolbar.Fill(), {
17548                 cls: 'x-btn-icon x-btn-clear',
17549                 text: '&#160;',
17550                 handler: function()
17551                 {
17552                     _this.collapse();
17553                     _this.clearValue();
17554                     _this.onSelect(false, -1);
17555                 }
17556             });
17557         }
17558         if (this.footer) {
17559             this.assetHeight += this.footer.getHeight();
17560         }
17561         */
17562             
17563         if(!this.tpl){
17564             this.tpl = Roo.bootstrap.version == 4 ?
17565                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17566                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17567         }
17568
17569         this.view = new Roo.View(this.list, this.tpl, {
17570             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17571         });
17572         //this.view.wrapEl.setDisplayed(false);
17573         this.view.on('click', this.onViewClick, this);
17574         
17575         
17576         this.store.on('beforeload', this.onBeforeLoad, this);
17577         this.store.on('load', this.onLoad, this);
17578         this.store.on('loadexception', this.onLoadException, this);
17579         /*
17580         if(this.resizable){
17581             this.resizer = new Roo.Resizable(this.list,  {
17582                pinned:true, handles:'se'
17583             });
17584             this.resizer.on('resize', function(r, w, h){
17585                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17586                 this.listWidth = w;
17587                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17588                 this.restrictHeight();
17589             }, this);
17590             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17591         }
17592         */
17593         if(!this.editable){
17594             this.editable = true;
17595             this.setEditable(false);
17596         }
17597         
17598         /*
17599         
17600         if (typeof(this.events.add.listeners) != 'undefined') {
17601             
17602             this.addicon = this.wrap.createChild(
17603                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17604        
17605             this.addicon.on('click', function(e) {
17606                 this.fireEvent('add', this);
17607             }, this);
17608         }
17609         if (typeof(this.events.edit.listeners) != 'undefined') {
17610             
17611             this.editicon = this.wrap.createChild(
17612                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17613             if (this.addicon) {
17614                 this.editicon.setStyle('margin-left', '40px');
17615             }
17616             this.editicon.on('click', function(e) {
17617                 
17618                 // we fire even  if inothing is selected..
17619                 this.fireEvent('edit', this, this.lastData );
17620                 
17621             }, this);
17622         }
17623         */
17624         
17625         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17626             "up" : function(e){
17627                 this.inKeyMode = true;
17628                 this.selectPrev();
17629             },
17630
17631             "down" : function(e){
17632                 if(!this.isExpanded()){
17633                     this.onTriggerClick();
17634                 }else{
17635                     this.inKeyMode = true;
17636                     this.selectNext();
17637                 }
17638             },
17639
17640             "enter" : function(e){
17641 //                this.onViewClick();
17642                 //return true;
17643                 this.collapse();
17644                 
17645                 if(this.fireEvent("specialkey", this, e)){
17646                     this.onViewClick(false);
17647                 }
17648                 
17649                 return true;
17650             },
17651
17652             "esc" : function(e){
17653                 this.collapse();
17654             },
17655
17656             "tab" : function(e){
17657                 this.collapse();
17658                 
17659                 if(this.fireEvent("specialkey", this, e)){
17660                     this.onViewClick(false);
17661                 }
17662                 
17663                 return true;
17664             },
17665
17666             scope : this,
17667
17668             doRelay : function(foo, bar, hname){
17669                 if(hname == 'down' || this.scope.isExpanded()){
17670                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17671                 }
17672                 return true;
17673             },
17674
17675             forceKeyDown: true
17676         });
17677         
17678         
17679         this.queryDelay = Math.max(this.queryDelay || 10,
17680                 this.mode == 'local' ? 10 : 250);
17681         
17682         
17683         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17684         
17685         if(this.typeAhead){
17686             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17687         }
17688         if(this.editable !== false){
17689             this.inputEl().on("keyup", this.onKeyUp, this);
17690         }
17691         if(this.forceSelection){
17692             this.inputEl().on('blur', this.doForce, this);
17693         }
17694         
17695         if(this.multiple){
17696             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17697             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17698         }
17699     },
17700     
17701     initTickableEvents: function()
17702     {   
17703         this.createList();
17704         
17705         if(this.hiddenName){
17706             
17707             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17708             
17709             this.hiddenField.dom.value =
17710                 this.hiddenValue !== undefined ? this.hiddenValue :
17711                 this.value !== undefined ? this.value : '';
17712
17713             // prevent input submission
17714             this.el.dom.removeAttribute('name');
17715             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17716              
17717              
17718         }
17719         
17720 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17721         
17722         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17723         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17724         if(this.triggerList){
17725             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17726         }
17727          
17728         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17729         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17730         
17731         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17732         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17733         
17734         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17735         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17736         
17737         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17738         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17739         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17740         
17741         this.okBtn.hide();
17742         this.cancelBtn.hide();
17743         
17744         var _this = this;
17745         
17746         (function(){
17747             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17748             _this.list.setWidth(lw);
17749         }).defer(100);
17750         
17751         this.list.on('mouseover', this.onViewOver, this);
17752         this.list.on('mousemove', this.onViewMove, this);
17753         
17754         this.list.on('scroll', this.onViewScroll, this);
17755         
17756         if(!this.tpl){
17757             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17758                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17759         }
17760
17761         this.view = new Roo.View(this.list, this.tpl, {
17762             singleSelect:true,
17763             tickable:true,
17764             parent:this,
17765             store: this.store,
17766             selectedClass: this.selectedClass
17767         });
17768         
17769         //this.view.wrapEl.setDisplayed(false);
17770         this.view.on('click', this.onViewClick, this);
17771         
17772         
17773         
17774         this.store.on('beforeload', this.onBeforeLoad, this);
17775         this.store.on('load', this.onLoad, this);
17776         this.store.on('loadexception', this.onLoadException, this);
17777         
17778         if(this.editable){
17779             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17780                 "up" : function(e){
17781                     this.inKeyMode = true;
17782                     this.selectPrev();
17783                 },
17784
17785                 "down" : function(e){
17786                     this.inKeyMode = true;
17787                     this.selectNext();
17788                 },
17789
17790                 "enter" : function(e){
17791                     if(this.fireEvent("specialkey", this, e)){
17792                         this.onViewClick(false);
17793                     }
17794                     
17795                     return true;
17796                 },
17797
17798                 "esc" : function(e){
17799                     this.onTickableFooterButtonClick(e, false, false);
17800                 },
17801
17802                 "tab" : function(e){
17803                     this.fireEvent("specialkey", this, e);
17804                     
17805                     this.onTickableFooterButtonClick(e, false, false);
17806                     
17807                     return true;
17808                 },
17809
17810                 scope : this,
17811
17812                 doRelay : function(e, fn, key){
17813                     if(this.scope.isExpanded()){
17814                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17815                     }
17816                     return true;
17817                 },
17818
17819                 forceKeyDown: true
17820             });
17821         }
17822         
17823         this.queryDelay = Math.max(this.queryDelay || 10,
17824                 this.mode == 'local' ? 10 : 250);
17825         
17826         
17827         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17828         
17829         if(this.typeAhead){
17830             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17831         }
17832         
17833         if(this.editable !== false){
17834             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17835         }
17836         
17837         this.indicator = this.indicatorEl();
17838         
17839         if(this.indicator){
17840             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17841             this.indicator.hide();
17842         }
17843         
17844     },
17845
17846     onDestroy : function(){
17847         if(this.view){
17848             this.view.setStore(null);
17849             this.view.el.removeAllListeners();
17850             this.view.el.remove();
17851             this.view.purgeListeners();
17852         }
17853         if(this.list){
17854             this.list.dom.innerHTML  = '';
17855         }
17856         
17857         if(this.store){
17858             this.store.un('beforeload', this.onBeforeLoad, this);
17859             this.store.un('load', this.onLoad, this);
17860             this.store.un('loadexception', this.onLoadException, this);
17861         }
17862         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17863     },
17864
17865     // private
17866     fireKey : function(e){
17867         if(e.isNavKeyPress() && !this.list.isVisible()){
17868             this.fireEvent("specialkey", this, e);
17869         }
17870     },
17871
17872     // private
17873     onResize: function(w, h)
17874     {
17875         
17876         
17877 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17878 //        
17879 //        if(typeof w != 'number'){
17880 //            // we do not handle it!?!?
17881 //            return;
17882 //        }
17883 //        var tw = this.trigger.getWidth();
17884 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17885 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17886 //        var x = w - tw;
17887 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17888 //            
17889 //        //this.trigger.setStyle('left', x+'px');
17890 //        
17891 //        if(this.list && this.listWidth === undefined){
17892 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17893 //            this.list.setWidth(lw);
17894 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17895 //        }
17896         
17897     
17898         
17899     },
17900
17901     /**
17902      * Allow or prevent the user from directly editing the field text.  If false is passed,
17903      * the user will only be able to select from the items defined in the dropdown list.  This method
17904      * is the runtime equivalent of setting the 'editable' config option at config time.
17905      * @param {Boolean} value True to allow the user to directly edit the field text
17906      */
17907     setEditable : function(value){
17908         if(value == this.editable){
17909             return;
17910         }
17911         this.editable = value;
17912         if(!value){
17913             this.inputEl().dom.setAttribute('readOnly', true);
17914             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17915             this.inputEl().addClass('x-combo-noedit');
17916         }else{
17917             this.inputEl().dom.removeAttribute('readOnly');
17918             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17919             this.inputEl().removeClass('x-combo-noedit');
17920         }
17921     },
17922
17923     // private
17924     
17925     onBeforeLoad : function(combo,opts){
17926         if(!this.hasFocus){
17927             return;
17928         }
17929          if (!opts.add) {
17930             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17931          }
17932         this.restrictHeight();
17933         this.selectedIndex = -1;
17934     },
17935
17936     // private
17937     onLoad : function(){
17938         
17939         this.hasQuery = false;
17940         
17941         if(!this.hasFocus){
17942             return;
17943         }
17944         
17945         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17946             this.loading.hide();
17947         }
17948         
17949         if(this.store.getCount() > 0){
17950             
17951             this.expand();
17952             this.restrictHeight();
17953             if(this.lastQuery == this.allQuery){
17954                 if(this.editable && !this.tickable){
17955                     this.inputEl().dom.select();
17956                 }
17957                 
17958                 if(
17959                     !this.selectByValue(this.value, true) &&
17960                     this.autoFocus && 
17961                     (
17962                         !this.store.lastOptions ||
17963                         typeof(this.store.lastOptions.add) == 'undefined' || 
17964                         this.store.lastOptions.add != true
17965                     )
17966                 ){
17967                     this.select(0, true);
17968                 }
17969             }else{
17970                 if(this.autoFocus){
17971                     this.selectNext();
17972                 }
17973                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17974                     this.taTask.delay(this.typeAheadDelay);
17975                 }
17976             }
17977         }else{
17978             this.onEmptyResults();
17979         }
17980         
17981         //this.el.focus();
17982     },
17983     // private
17984     onLoadException : function()
17985     {
17986         this.hasQuery = false;
17987         
17988         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17989             this.loading.hide();
17990         }
17991         
17992         if(this.tickable && this.editable){
17993             return;
17994         }
17995         
17996         this.collapse();
17997         // only causes errors at present
17998         //Roo.log(this.store.reader.jsonData);
17999         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18000             // fixme
18001             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18002         //}
18003         
18004         
18005     },
18006     // private
18007     onTypeAhead : function(){
18008         if(this.store.getCount() > 0){
18009             var r = this.store.getAt(0);
18010             var newValue = r.data[this.displayField];
18011             var len = newValue.length;
18012             var selStart = this.getRawValue().length;
18013             
18014             if(selStart != len){
18015                 this.setRawValue(newValue);
18016                 this.selectText(selStart, newValue.length);
18017             }
18018         }
18019     },
18020
18021     // private
18022     onSelect : function(record, index){
18023         
18024         if(this.fireEvent('beforeselect', this, record, index) !== false){
18025         
18026             this.setFromData(index > -1 ? record.data : false);
18027             
18028             this.collapse();
18029             this.fireEvent('select', this, record, index);
18030         }
18031     },
18032
18033     /**
18034      * Returns the currently selected field value or empty string if no value is set.
18035      * @return {String} value The selected value
18036      */
18037     getValue : function()
18038     {
18039         if(Roo.isIOS && this.useNativeIOS){
18040             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18041         }
18042         
18043         if(this.multiple){
18044             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18045         }
18046         
18047         if(this.valueField){
18048             return typeof this.value != 'undefined' ? this.value : '';
18049         }else{
18050             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18051         }
18052     },
18053     
18054     getRawValue : function()
18055     {
18056         if(Roo.isIOS && this.useNativeIOS){
18057             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18058         }
18059         
18060         var v = this.inputEl().getValue();
18061         
18062         return v;
18063     },
18064
18065     /**
18066      * Clears any text/value currently set in the field
18067      */
18068     clearValue : function(){
18069         
18070         if(this.hiddenField){
18071             this.hiddenField.dom.value = '';
18072         }
18073         this.value = '';
18074         this.setRawValue('');
18075         this.lastSelectionText = '';
18076         this.lastData = false;
18077         
18078         var close = this.closeTriggerEl();
18079         
18080         if(close){
18081             close.hide();
18082         }
18083         
18084         this.validate();
18085         
18086     },
18087
18088     /**
18089      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18090      * will be displayed in the field.  If the value does not match the data value of an existing item,
18091      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18092      * Otherwise the field will be blank (although the value will still be set).
18093      * @param {String} value The value to match
18094      */
18095     setValue : function(v)
18096     {
18097         if(Roo.isIOS && this.useNativeIOS){
18098             this.setIOSValue(v);
18099             return;
18100         }
18101         
18102         if(this.multiple){
18103             this.syncValue();
18104             return;
18105         }
18106         
18107         var text = v;
18108         if(this.valueField){
18109             var r = this.findRecord(this.valueField, v);
18110             if(r){
18111                 text = r.data[this.displayField];
18112             }else if(this.valueNotFoundText !== undefined){
18113                 text = this.valueNotFoundText;
18114             }
18115         }
18116         this.lastSelectionText = text;
18117         if(this.hiddenField){
18118             this.hiddenField.dom.value = v;
18119         }
18120         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18121         this.value = v;
18122         
18123         var close = this.closeTriggerEl();
18124         
18125         if(close){
18126             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18127         }
18128         
18129         this.validate();
18130     },
18131     /**
18132      * @property {Object} the last set data for the element
18133      */
18134     
18135     lastData : false,
18136     /**
18137      * Sets the value of the field based on a object which is related to the record format for the store.
18138      * @param {Object} value the value to set as. or false on reset?
18139      */
18140     setFromData : function(o){
18141         
18142         if(this.multiple){
18143             this.addItem(o);
18144             return;
18145         }
18146             
18147         var dv = ''; // display value
18148         var vv = ''; // value value..
18149         this.lastData = o;
18150         if (this.displayField) {
18151             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18152         } else {
18153             // this is an error condition!!!
18154             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18155         }
18156         
18157         if(this.valueField){
18158             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18159         }
18160         
18161         var close = this.closeTriggerEl();
18162         
18163         if(close){
18164             if(dv.length || vv * 1 > 0){
18165                 close.show() ;
18166                 this.blockFocus=true;
18167             } else {
18168                 close.hide();
18169             }             
18170         }
18171         
18172         if(this.hiddenField){
18173             this.hiddenField.dom.value = vv;
18174             
18175             this.lastSelectionText = dv;
18176             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18177             this.value = vv;
18178             return;
18179         }
18180         // no hidden field.. - we store the value in 'value', but still display
18181         // display field!!!!
18182         this.lastSelectionText = dv;
18183         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18184         this.value = vv;
18185         
18186         
18187         
18188     },
18189     // private
18190     reset : function(){
18191         // overridden so that last data is reset..
18192         
18193         if(this.multiple){
18194             this.clearItem();
18195             return;
18196         }
18197         
18198         this.setValue(this.originalValue);
18199         //this.clearInvalid();
18200         this.lastData = false;
18201         if (this.view) {
18202             this.view.clearSelections();
18203         }
18204         
18205         this.validate();
18206     },
18207     // private
18208     findRecord : function(prop, value){
18209         var record;
18210         if(this.store.getCount() > 0){
18211             this.store.each(function(r){
18212                 if(r.data[prop] == value){
18213                     record = r;
18214                     return false;
18215                 }
18216                 return true;
18217             });
18218         }
18219         return record;
18220     },
18221     
18222     getName: function()
18223     {
18224         // returns hidden if it's set..
18225         if (!this.rendered) {return ''};
18226         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18227         
18228     },
18229     // private
18230     onViewMove : function(e, t){
18231         this.inKeyMode = false;
18232     },
18233
18234     // private
18235     onViewOver : function(e, t){
18236         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18237             return;
18238         }
18239         var item = this.view.findItemFromChild(t);
18240         
18241         if(item){
18242             var index = this.view.indexOf(item);
18243             this.select(index, false);
18244         }
18245     },
18246
18247     // private
18248     onViewClick : function(view, doFocus, el, e)
18249     {
18250         var index = this.view.getSelectedIndexes()[0];
18251         
18252         var r = this.store.getAt(index);
18253         
18254         if(this.tickable){
18255             
18256             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18257                 return;
18258             }
18259             
18260             var rm = false;
18261             var _this = this;
18262             
18263             Roo.each(this.tickItems, function(v,k){
18264                 
18265                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18266                     Roo.log(v);
18267                     _this.tickItems.splice(k, 1);
18268                     
18269                     if(typeof(e) == 'undefined' && view == false){
18270                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18271                     }
18272                     
18273                     rm = true;
18274                     return;
18275                 }
18276             });
18277             
18278             if(rm){
18279                 return;
18280             }
18281             
18282             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18283                 this.tickItems.push(r.data);
18284             }
18285             
18286             if(typeof(e) == 'undefined' && view == false){
18287                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18288             }
18289                     
18290             return;
18291         }
18292         
18293         if(r){
18294             this.onSelect(r, index);
18295         }
18296         if(doFocus !== false && !this.blockFocus){
18297             this.inputEl().focus();
18298         }
18299     },
18300
18301     // private
18302     restrictHeight : function(){
18303         //this.innerList.dom.style.height = '';
18304         //var inner = this.innerList.dom;
18305         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18306         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18307         //this.list.beginUpdate();
18308         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18309         this.list.alignTo(this.inputEl(), this.listAlign);
18310         this.list.alignTo(this.inputEl(), this.listAlign);
18311         //this.list.endUpdate();
18312     },
18313
18314     // private
18315     onEmptyResults : function(){
18316         
18317         if(this.tickable && this.editable){
18318             this.hasFocus = false;
18319             this.restrictHeight();
18320             return;
18321         }
18322         
18323         this.collapse();
18324     },
18325
18326     /**
18327      * Returns true if the dropdown list is expanded, else false.
18328      */
18329     isExpanded : function(){
18330         return this.list.isVisible();
18331     },
18332
18333     /**
18334      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18335      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18336      * @param {String} value The data value of the item to select
18337      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18338      * selected item if it is not currently in view (defaults to true)
18339      * @return {Boolean} True if the value matched an item in the list, else false
18340      */
18341     selectByValue : function(v, scrollIntoView){
18342         if(v !== undefined && v !== null){
18343             var r = this.findRecord(this.valueField || this.displayField, v);
18344             if(r){
18345                 this.select(this.store.indexOf(r), scrollIntoView);
18346                 return true;
18347             }
18348         }
18349         return false;
18350     },
18351
18352     /**
18353      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18354      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18355      * @param {Number} index The zero-based index of the list item to select
18356      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18357      * selected item if it is not currently in view (defaults to true)
18358      */
18359     select : function(index, scrollIntoView){
18360         this.selectedIndex = index;
18361         this.view.select(index);
18362         if(scrollIntoView !== false){
18363             var el = this.view.getNode(index);
18364             /*
18365              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18366              */
18367             if(el){
18368                 this.list.scrollChildIntoView(el, false);
18369             }
18370         }
18371     },
18372
18373     // private
18374     selectNext : function(){
18375         var ct = this.store.getCount();
18376         if(ct > 0){
18377             if(this.selectedIndex == -1){
18378                 this.select(0);
18379             }else if(this.selectedIndex < ct-1){
18380                 this.select(this.selectedIndex+1);
18381             }
18382         }
18383     },
18384
18385     // private
18386     selectPrev : function(){
18387         var ct = this.store.getCount();
18388         if(ct > 0){
18389             if(this.selectedIndex == -1){
18390                 this.select(0);
18391             }else if(this.selectedIndex != 0){
18392                 this.select(this.selectedIndex-1);
18393             }
18394         }
18395     },
18396
18397     // private
18398     onKeyUp : function(e){
18399         if(this.editable !== false && !e.isSpecialKey()){
18400             this.lastKey = e.getKey();
18401             this.dqTask.delay(this.queryDelay);
18402         }
18403     },
18404
18405     // private
18406     validateBlur : function(){
18407         return !this.list || !this.list.isVisible();   
18408     },
18409
18410     // private
18411     initQuery : function(){
18412         
18413         var v = this.getRawValue();
18414         
18415         if(this.tickable && this.editable){
18416             v = this.tickableInputEl().getValue();
18417         }
18418         
18419         this.doQuery(v);
18420     },
18421
18422     // private
18423     doForce : function(){
18424         if(this.inputEl().dom.value.length > 0){
18425             this.inputEl().dom.value =
18426                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18427              
18428         }
18429     },
18430
18431     /**
18432      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18433      * query allowing the query action to be canceled if needed.
18434      * @param {String} query The SQL query to execute
18435      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18436      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18437      * saved in the current store (defaults to false)
18438      */
18439     doQuery : function(q, forceAll){
18440         
18441         if(q === undefined || q === null){
18442             q = '';
18443         }
18444         var qe = {
18445             query: q,
18446             forceAll: forceAll,
18447             combo: this,
18448             cancel:false
18449         };
18450         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18451             return false;
18452         }
18453         q = qe.query;
18454         
18455         forceAll = qe.forceAll;
18456         if(forceAll === true || (q.length >= this.minChars)){
18457             
18458             this.hasQuery = true;
18459             
18460             if(this.lastQuery != q || this.alwaysQuery){
18461                 this.lastQuery = q;
18462                 if(this.mode == 'local'){
18463                     this.selectedIndex = -1;
18464                     if(forceAll){
18465                         this.store.clearFilter();
18466                     }else{
18467                         
18468                         if(this.specialFilter){
18469                             this.fireEvent('specialfilter', this);
18470                             this.onLoad();
18471                             return;
18472                         }
18473                         
18474                         this.store.filter(this.displayField, q);
18475                     }
18476                     
18477                     this.store.fireEvent("datachanged", this.store);
18478                     
18479                     this.onLoad();
18480                     
18481                     
18482                 }else{
18483                     
18484                     this.store.baseParams[this.queryParam] = q;
18485                     
18486                     var options = {params : this.getParams(q)};
18487                     
18488                     if(this.loadNext){
18489                         options.add = true;
18490                         options.params.start = this.page * this.pageSize;
18491                     }
18492                     
18493                     this.store.load(options);
18494                     
18495                     /*
18496                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18497                      *  we should expand the list on onLoad
18498                      *  so command out it
18499                      */
18500 //                    this.expand();
18501                 }
18502             }else{
18503                 this.selectedIndex = -1;
18504                 this.onLoad();   
18505             }
18506         }
18507         
18508         this.loadNext = false;
18509     },
18510     
18511     // private
18512     getParams : function(q){
18513         var p = {};
18514         //p[this.queryParam] = q;
18515         
18516         if(this.pageSize){
18517             p.start = 0;
18518             p.limit = this.pageSize;
18519         }
18520         return p;
18521     },
18522
18523     /**
18524      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18525      */
18526     collapse : function(){
18527         if(!this.isExpanded()){
18528             return;
18529         }
18530         
18531         this.list.hide();
18532         
18533         this.hasFocus = false;
18534         
18535         if(this.tickable){
18536             this.okBtn.hide();
18537             this.cancelBtn.hide();
18538             this.trigger.show();
18539             
18540             if(this.editable){
18541                 this.tickableInputEl().dom.value = '';
18542                 this.tickableInputEl().blur();
18543             }
18544             
18545         }
18546         
18547         Roo.get(document).un('mousedown', this.collapseIf, this);
18548         Roo.get(document).un('mousewheel', this.collapseIf, this);
18549         if (!this.editable) {
18550             Roo.get(document).un('keydown', this.listKeyPress, this);
18551         }
18552         this.fireEvent('collapse', this);
18553         
18554         this.validate();
18555     },
18556
18557     // private
18558     collapseIf : function(e){
18559         var in_combo  = e.within(this.el);
18560         var in_list =  e.within(this.list);
18561         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18562         
18563         if (in_combo || in_list || is_list) {
18564             //e.stopPropagation();
18565             return;
18566         }
18567         
18568         if(this.tickable){
18569             this.onTickableFooterButtonClick(e, false, false);
18570         }
18571
18572         this.collapse();
18573         
18574     },
18575
18576     /**
18577      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18578      */
18579     expand : function(){
18580        
18581         if(this.isExpanded() || !this.hasFocus){
18582             return;
18583         }
18584         
18585         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18586         this.list.setWidth(lw);
18587         
18588         Roo.log('expand');
18589         
18590         this.list.show();
18591         
18592         this.restrictHeight();
18593         
18594         if(this.tickable){
18595             
18596             this.tickItems = Roo.apply([], this.item);
18597             
18598             this.okBtn.show();
18599             this.cancelBtn.show();
18600             this.trigger.hide();
18601             
18602             if(this.editable){
18603                 this.tickableInputEl().focus();
18604             }
18605             
18606         }
18607         
18608         Roo.get(document).on('mousedown', this.collapseIf, this);
18609         Roo.get(document).on('mousewheel', this.collapseIf, this);
18610         if (!this.editable) {
18611             Roo.get(document).on('keydown', this.listKeyPress, this);
18612         }
18613         
18614         this.fireEvent('expand', this);
18615     },
18616
18617     // private
18618     // Implements the default empty TriggerField.onTriggerClick function
18619     onTriggerClick : function(e)
18620     {
18621         Roo.log('trigger click');
18622         
18623         if(this.disabled || !this.triggerList){
18624             return;
18625         }
18626         
18627         this.page = 0;
18628         this.loadNext = false;
18629         
18630         if(this.isExpanded()){
18631             this.collapse();
18632             if (!this.blockFocus) {
18633                 this.inputEl().focus();
18634             }
18635             
18636         }else {
18637             this.hasFocus = true;
18638             if(this.triggerAction == 'all') {
18639                 this.doQuery(this.allQuery, true);
18640             } else {
18641                 this.doQuery(this.getRawValue());
18642             }
18643             if (!this.blockFocus) {
18644                 this.inputEl().focus();
18645             }
18646         }
18647     },
18648     
18649     onTickableTriggerClick : function(e)
18650     {
18651         if(this.disabled){
18652             return;
18653         }
18654         
18655         this.page = 0;
18656         this.loadNext = false;
18657         this.hasFocus = true;
18658         
18659         if(this.triggerAction == 'all') {
18660             this.doQuery(this.allQuery, true);
18661         } else {
18662             this.doQuery(this.getRawValue());
18663         }
18664     },
18665     
18666     onSearchFieldClick : function(e)
18667     {
18668         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18669             this.onTickableFooterButtonClick(e, false, false);
18670             return;
18671         }
18672         
18673         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18674             return;
18675         }
18676         
18677         this.page = 0;
18678         this.loadNext = false;
18679         this.hasFocus = true;
18680         
18681         if(this.triggerAction == 'all') {
18682             this.doQuery(this.allQuery, true);
18683         } else {
18684             this.doQuery(this.getRawValue());
18685         }
18686     },
18687     
18688     listKeyPress : function(e)
18689     {
18690         //Roo.log('listkeypress');
18691         // scroll to first matching element based on key pres..
18692         if (e.isSpecialKey()) {
18693             return false;
18694         }
18695         var k = String.fromCharCode(e.getKey()).toUpperCase();
18696         //Roo.log(k);
18697         var match  = false;
18698         var csel = this.view.getSelectedNodes();
18699         var cselitem = false;
18700         if (csel.length) {
18701             var ix = this.view.indexOf(csel[0]);
18702             cselitem  = this.store.getAt(ix);
18703             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18704                 cselitem = false;
18705             }
18706             
18707         }
18708         
18709         this.store.each(function(v) { 
18710             if (cselitem) {
18711                 // start at existing selection.
18712                 if (cselitem.id == v.id) {
18713                     cselitem = false;
18714                 }
18715                 return true;
18716             }
18717                 
18718             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18719                 match = this.store.indexOf(v);
18720                 return false;
18721             }
18722             return true;
18723         }, this);
18724         
18725         if (match === false) {
18726             return true; // no more action?
18727         }
18728         // scroll to?
18729         this.view.select(match);
18730         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18731         sn.scrollIntoView(sn.dom.parentNode, false);
18732     },
18733     
18734     onViewScroll : function(e, t){
18735         
18736         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){
18737             return;
18738         }
18739         
18740         this.hasQuery = true;
18741         
18742         this.loading = this.list.select('.loading', true).first();
18743         
18744         if(this.loading === null){
18745             this.list.createChild({
18746                 tag: 'div',
18747                 cls: 'loading roo-select2-more-results roo-select2-active',
18748                 html: 'Loading more results...'
18749             });
18750             
18751             this.loading = this.list.select('.loading', true).first();
18752             
18753             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18754             
18755             this.loading.hide();
18756         }
18757         
18758         this.loading.show();
18759         
18760         var _combo = this;
18761         
18762         this.page++;
18763         this.loadNext = true;
18764         
18765         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18766         
18767         return;
18768     },
18769     
18770     addItem : function(o)
18771     {   
18772         var dv = ''; // display value
18773         
18774         if (this.displayField) {
18775             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18776         } else {
18777             // this is an error condition!!!
18778             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18779         }
18780         
18781         if(!dv.length){
18782             return;
18783         }
18784         
18785         var choice = this.choices.createChild({
18786             tag: 'li',
18787             cls: 'roo-select2-search-choice',
18788             cn: [
18789                 {
18790                     tag: 'div',
18791                     html: dv
18792                 },
18793                 {
18794                     tag: 'a',
18795                     href: '#',
18796                     cls: 'roo-select2-search-choice-close fa fa-times',
18797                     tabindex: '-1'
18798                 }
18799             ]
18800             
18801         }, this.searchField);
18802         
18803         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18804         
18805         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18806         
18807         this.item.push(o);
18808         
18809         this.lastData = o;
18810         
18811         this.syncValue();
18812         
18813         this.inputEl().dom.value = '';
18814         
18815         this.validate();
18816     },
18817     
18818     onRemoveItem : function(e, _self, o)
18819     {
18820         e.preventDefault();
18821         
18822         this.lastItem = Roo.apply([], this.item);
18823         
18824         var index = this.item.indexOf(o.data) * 1;
18825         
18826         if( index < 0){
18827             Roo.log('not this item?!');
18828             return;
18829         }
18830         
18831         this.item.splice(index, 1);
18832         o.item.remove();
18833         
18834         this.syncValue();
18835         
18836         this.fireEvent('remove', this, e);
18837         
18838         this.validate();
18839         
18840     },
18841     
18842     syncValue : function()
18843     {
18844         if(!this.item.length){
18845             this.clearValue();
18846             return;
18847         }
18848             
18849         var value = [];
18850         var _this = this;
18851         Roo.each(this.item, function(i){
18852             if(_this.valueField){
18853                 value.push(i[_this.valueField]);
18854                 return;
18855             }
18856
18857             value.push(i);
18858         });
18859
18860         this.value = value.join(',');
18861
18862         if(this.hiddenField){
18863             this.hiddenField.dom.value = this.value;
18864         }
18865         
18866         this.store.fireEvent("datachanged", this.store);
18867         
18868         this.validate();
18869     },
18870     
18871     clearItem : function()
18872     {
18873         if(!this.multiple){
18874             return;
18875         }
18876         
18877         this.item = [];
18878         
18879         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18880            c.remove();
18881         });
18882         
18883         this.syncValue();
18884         
18885         this.validate();
18886         
18887         if(this.tickable && !Roo.isTouch){
18888             this.view.refresh();
18889         }
18890     },
18891     
18892     inputEl: function ()
18893     {
18894         if(Roo.isIOS && this.useNativeIOS){
18895             return this.el.select('select.roo-ios-select', true).first();
18896         }
18897         
18898         if(Roo.isTouch && this.mobileTouchView){
18899             return this.el.select('input.form-control',true).first();
18900         }
18901         
18902         if(this.tickable){
18903             return this.searchField;
18904         }
18905         
18906         return this.el.select('input.form-control',true).first();
18907     },
18908     
18909     onTickableFooterButtonClick : function(e, btn, el)
18910     {
18911         e.preventDefault();
18912         
18913         this.lastItem = Roo.apply([], this.item);
18914         
18915         if(btn && btn.name == 'cancel'){
18916             this.tickItems = Roo.apply([], this.item);
18917             this.collapse();
18918             return;
18919         }
18920         
18921         this.clearItem();
18922         
18923         var _this = this;
18924         
18925         Roo.each(this.tickItems, function(o){
18926             _this.addItem(o);
18927         });
18928         
18929         this.collapse();
18930         
18931     },
18932     
18933     validate : function()
18934     {
18935         if(this.getVisibilityEl().hasClass('hidden')){
18936             return true;
18937         }
18938         
18939         var v = this.getRawValue();
18940         
18941         if(this.multiple){
18942             v = this.getValue();
18943         }
18944         
18945         if(this.disabled || this.allowBlank || v.length){
18946             this.markValid();
18947             return true;
18948         }
18949         
18950         this.markInvalid();
18951         return false;
18952     },
18953     
18954     tickableInputEl : function()
18955     {
18956         if(!this.tickable || !this.editable){
18957             return this.inputEl();
18958         }
18959         
18960         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18961     },
18962     
18963     
18964     getAutoCreateTouchView : function()
18965     {
18966         var id = Roo.id();
18967         
18968         var cfg = {
18969             cls: 'form-group' //input-group
18970         };
18971         
18972         var input =  {
18973             tag: 'input',
18974             id : id,
18975             type : this.inputType,
18976             cls : 'form-control x-combo-noedit',
18977             autocomplete: 'new-password',
18978             placeholder : this.placeholder || '',
18979             readonly : true
18980         };
18981         
18982         if (this.name) {
18983             input.name = this.name;
18984         }
18985         
18986         if (this.size) {
18987             input.cls += ' input-' + this.size;
18988         }
18989         
18990         if (this.disabled) {
18991             input.disabled = true;
18992         }
18993         
18994         var inputblock = {
18995             cls : 'roo-combobox-wrap',
18996             cn : [
18997                 input
18998             ]
18999         };
19000         
19001         if(this.before){
19002             inputblock.cls += ' input-group';
19003             
19004             inputblock.cn.unshift({
19005                 tag :'span',
19006                 cls : 'input-group-addon input-group-prepend input-group-text',
19007                 html : this.before
19008             });
19009         }
19010         
19011         if(this.removable && !this.multiple){
19012             inputblock.cls += ' roo-removable';
19013             
19014             inputblock.cn.push({
19015                 tag: 'button',
19016                 html : 'x',
19017                 cls : 'roo-combo-removable-btn close'
19018             });
19019         }
19020
19021         if(this.hasFeedback && !this.allowBlank){
19022             
19023             inputblock.cls += ' has-feedback';
19024             
19025             inputblock.cn.push({
19026                 tag: 'span',
19027                 cls: 'glyphicon form-control-feedback'
19028             });
19029             
19030         }
19031         
19032         if (this.after) {
19033             
19034             inputblock.cls += (this.before) ? '' : ' input-group';
19035             
19036             inputblock.cn.push({
19037                 tag :'span',
19038                 cls : 'input-group-addon input-group-append input-group-text',
19039                 html : this.after
19040             });
19041         }
19042
19043         
19044         var ibwrap = inputblock;
19045         
19046         if(this.multiple){
19047             ibwrap = {
19048                 tag: 'ul',
19049                 cls: 'roo-select2-choices',
19050                 cn:[
19051                     {
19052                         tag: 'li',
19053                         cls: 'roo-select2-search-field',
19054                         cn: [
19055
19056                             inputblock
19057                         ]
19058                     }
19059                 ]
19060             };
19061         
19062             
19063         }
19064         
19065         var combobox = {
19066             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19067             cn: [
19068                 {
19069                     tag: 'input',
19070                     type : 'hidden',
19071                     cls: 'form-hidden-field'
19072                 },
19073                 ibwrap
19074             ]
19075         };
19076         
19077         if(!this.multiple && this.showToggleBtn){
19078             
19079             var caret = {
19080                 cls: 'caret'
19081             };
19082             
19083             if (this.caret != false) {
19084                 caret = {
19085                      tag: 'i',
19086                      cls: 'fa fa-' + this.caret
19087                 };
19088                 
19089             }
19090             
19091             combobox.cn.push({
19092                 tag :'span',
19093                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19094                 cn : [
19095                     Roo.bootstrap.version == 3 ? caret : '',
19096                     {
19097                         tag: 'span',
19098                         cls: 'combobox-clear',
19099                         cn  : [
19100                             {
19101                                 tag : 'i',
19102                                 cls: 'icon-remove'
19103                             }
19104                         ]
19105                     }
19106                 ]
19107
19108             })
19109         }
19110         
19111         if(this.multiple){
19112             combobox.cls += ' roo-select2-container-multi';
19113         }
19114         
19115         var required =  this.allowBlank ?  {
19116                     tag : 'i',
19117                     style: 'display: none'
19118                 } : {
19119                    tag : 'i',
19120                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19121                    tooltip : 'This field is required'
19122                 };
19123         
19124         var align = this.labelAlign || this.parentLabelAlign();
19125         
19126         if (align ==='left' && this.fieldLabel.length) {
19127
19128             cfg.cn = [
19129                 required,
19130                 {
19131                     tag: 'label',
19132                     cls : 'control-label col-form-label',
19133                     html : this.fieldLabel
19134
19135                 },
19136                 {
19137                     cls : 'roo-combobox-wrap ', 
19138                     cn: [
19139                         combobox
19140                     ]
19141                 }
19142             ];
19143             
19144             var labelCfg = cfg.cn[1];
19145             var contentCfg = cfg.cn[2];
19146             
19147
19148             if(this.indicatorpos == 'right'){
19149                 cfg.cn = [
19150                     {
19151                         tag: 'label',
19152                         'for' :  id,
19153                         cls : 'control-label col-form-label',
19154                         cn : [
19155                             {
19156                                 tag : 'span',
19157                                 html : this.fieldLabel
19158                             },
19159                             required
19160                         ]
19161                     },
19162                     {
19163                         cls : "roo-combobox-wrap ",
19164                         cn: [
19165                             combobox
19166                         ]
19167                     }
19168
19169                 ];
19170                 
19171                 labelCfg = cfg.cn[0];
19172                 contentCfg = cfg.cn[1];
19173             }
19174             
19175            
19176             
19177             if(this.labelWidth > 12){
19178                 labelCfg.style = "width: " + this.labelWidth + 'px';
19179             }
19180            
19181             if(this.labelWidth < 13 && this.labelmd == 0){
19182                 this.labelmd = this.labelWidth;
19183             }
19184             
19185             if(this.labellg > 0){
19186                 labelCfg.cls += ' col-lg-' + this.labellg;
19187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19188             }
19189             
19190             if(this.labelmd > 0){
19191                 labelCfg.cls += ' col-md-' + this.labelmd;
19192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19193             }
19194             
19195             if(this.labelsm > 0){
19196                 labelCfg.cls += ' col-sm-' + this.labelsm;
19197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19198             }
19199             
19200             if(this.labelxs > 0){
19201                 labelCfg.cls += ' col-xs-' + this.labelxs;
19202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19203             }
19204                 
19205                 
19206         } else if ( this.fieldLabel.length) {
19207             cfg.cn = [
19208                required,
19209                 {
19210                     tag: 'label',
19211                     cls : 'control-label',
19212                     html : this.fieldLabel
19213
19214                 },
19215                 {
19216                     cls : '', 
19217                     cn: [
19218                         combobox
19219                     ]
19220                 }
19221             ];
19222             
19223             if(this.indicatorpos == 'right'){
19224                 cfg.cn = [
19225                     {
19226                         tag: 'label',
19227                         cls : 'control-label',
19228                         html : this.fieldLabel,
19229                         cn : [
19230                             required
19231                         ]
19232                     },
19233                     {
19234                         cls : '', 
19235                         cn: [
19236                             combobox
19237                         ]
19238                     }
19239                 ];
19240             }
19241         } else {
19242             cfg.cn = combobox;    
19243         }
19244         
19245         
19246         var settings = this;
19247         
19248         ['xs','sm','md','lg'].map(function(size){
19249             if (settings[size]) {
19250                 cfg.cls += ' col-' + size + '-' + settings[size];
19251             }
19252         });
19253         
19254         return cfg;
19255     },
19256     
19257     initTouchView : function()
19258     {
19259         this.renderTouchView();
19260         
19261         this.touchViewEl.on('scroll', function(){
19262             this.el.dom.scrollTop = 0;
19263         }, this);
19264         
19265         this.originalValue = this.getValue();
19266         
19267         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19268         
19269         this.inputEl().on("click", this.showTouchView, this);
19270         if (this.triggerEl) {
19271             this.triggerEl.on("click", this.showTouchView, this);
19272         }
19273         
19274         
19275         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19276         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19277         
19278         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19279         
19280         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19281         this.store.on('load', this.onTouchViewLoad, this);
19282         this.store.on('loadexception', this.onTouchViewLoadException, this);
19283         
19284         if(this.hiddenName){
19285             
19286             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19287             
19288             this.hiddenField.dom.value =
19289                 this.hiddenValue !== undefined ? this.hiddenValue :
19290                 this.value !== undefined ? this.value : '';
19291         
19292             this.el.dom.removeAttribute('name');
19293             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19294         }
19295         
19296         if(this.multiple){
19297             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19298             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19299         }
19300         
19301         if(this.removable && !this.multiple){
19302             var close = this.closeTriggerEl();
19303             if(close){
19304                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19305                 close.on('click', this.removeBtnClick, this, close);
19306             }
19307         }
19308         /*
19309          * fix the bug in Safari iOS8
19310          */
19311         this.inputEl().on("focus", function(e){
19312             document.activeElement.blur();
19313         }, this);
19314         
19315         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19316         
19317         return;
19318         
19319         
19320     },
19321     
19322     renderTouchView : function()
19323     {
19324         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19325         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19326         
19327         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19328         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19329         
19330         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19331         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19332         this.touchViewBodyEl.setStyle('overflow', 'auto');
19333         
19334         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19335         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19336         
19337         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19338         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19339         
19340     },
19341     
19342     showTouchView : function()
19343     {
19344         if(this.disabled){
19345             return;
19346         }
19347         
19348         this.touchViewHeaderEl.hide();
19349
19350         if(this.modalTitle.length){
19351             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19352             this.touchViewHeaderEl.show();
19353         }
19354
19355         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19356         this.touchViewEl.show();
19357
19358         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19359         
19360         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19361         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19362
19363         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19364
19365         if(this.modalTitle.length){
19366             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19367         }
19368         
19369         this.touchViewBodyEl.setHeight(bodyHeight);
19370
19371         if(this.animate){
19372             var _this = this;
19373             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19374         }else{
19375             this.touchViewEl.addClass(['in','show']);
19376         }
19377         
19378         if(this._touchViewMask){
19379             Roo.get(document.body).addClass("x-body-masked");
19380             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19381             this._touchViewMask.setStyle('z-index', 10000);
19382             this._touchViewMask.addClass('show');
19383         }
19384         
19385         this.doTouchViewQuery();
19386         
19387     },
19388     
19389     hideTouchView : function()
19390     {
19391         this.touchViewEl.removeClass(['in','show']);
19392
19393         if(this.animate){
19394             var _this = this;
19395             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19396         }else{
19397             this.touchViewEl.setStyle('display', 'none');
19398         }
19399         
19400         if(this._touchViewMask){
19401             this._touchViewMask.removeClass('show');
19402             Roo.get(document.body).removeClass("x-body-masked");
19403         }
19404     },
19405     
19406     setTouchViewValue : function()
19407     {
19408         if(this.multiple){
19409             this.clearItem();
19410         
19411             var _this = this;
19412
19413             Roo.each(this.tickItems, function(o){
19414                 this.addItem(o);
19415             }, this);
19416         }
19417         
19418         this.hideTouchView();
19419     },
19420     
19421     doTouchViewQuery : function()
19422     {
19423         var qe = {
19424             query: '',
19425             forceAll: true,
19426             combo: this,
19427             cancel:false
19428         };
19429         
19430         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19431             return false;
19432         }
19433         
19434         if(!this.alwaysQuery || this.mode == 'local'){
19435             this.onTouchViewLoad();
19436             return;
19437         }
19438         
19439         this.store.load();
19440     },
19441     
19442     onTouchViewBeforeLoad : function(combo,opts)
19443     {
19444         return;
19445     },
19446
19447     // private
19448     onTouchViewLoad : function()
19449     {
19450         if(this.store.getCount() < 1){
19451             this.onTouchViewEmptyResults();
19452             return;
19453         }
19454         
19455         this.clearTouchView();
19456         
19457         var rawValue = this.getRawValue();
19458         
19459         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19460         
19461         this.tickItems = [];
19462         
19463         this.store.data.each(function(d, rowIndex){
19464             var row = this.touchViewListGroup.createChild(template);
19465             
19466             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19467                 row.addClass(d.data.cls);
19468             }
19469             
19470             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19471                 var cfg = {
19472                     data : d.data,
19473                     html : d.data[this.displayField]
19474                 };
19475                 
19476                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19477                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19478                 }
19479             }
19480             row.removeClass('selected');
19481             if(!this.multiple && this.valueField &&
19482                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19483             {
19484                 // radio buttons..
19485                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19486                 row.addClass('selected');
19487             }
19488             
19489             if(this.multiple && this.valueField &&
19490                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19491             {
19492                 
19493                 // checkboxes...
19494                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19495                 this.tickItems.push(d.data);
19496             }
19497             
19498             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19499             
19500         }, this);
19501         
19502         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19503         
19504         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19505
19506         if(this.modalTitle.length){
19507             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19508         }
19509
19510         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19511         
19512         if(this.mobile_restrict_height && listHeight < bodyHeight){
19513             this.touchViewBodyEl.setHeight(listHeight);
19514         }
19515         
19516         var _this = this;
19517         
19518         if(firstChecked && listHeight > bodyHeight){
19519             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19520         }
19521         
19522     },
19523     
19524     onTouchViewLoadException : function()
19525     {
19526         this.hideTouchView();
19527     },
19528     
19529     onTouchViewEmptyResults : function()
19530     {
19531         this.clearTouchView();
19532         
19533         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19534         
19535         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19536         
19537     },
19538     
19539     clearTouchView : function()
19540     {
19541         this.touchViewListGroup.dom.innerHTML = '';
19542     },
19543     
19544     onTouchViewClick : function(e, el, o)
19545     {
19546         e.preventDefault();
19547         
19548         var row = o.row;
19549         var rowIndex = o.rowIndex;
19550         
19551         var r = this.store.getAt(rowIndex);
19552         
19553         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19554             
19555             if(!this.multiple){
19556                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19557                     c.dom.removeAttribute('checked');
19558                 }, this);
19559
19560                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19561
19562                 this.setFromData(r.data);
19563
19564                 var close = this.closeTriggerEl();
19565
19566                 if(close){
19567                     close.show();
19568                 }
19569
19570                 this.hideTouchView();
19571
19572                 this.fireEvent('select', this, r, rowIndex);
19573
19574                 return;
19575             }
19576
19577             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19578                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19579                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19580                 return;
19581             }
19582
19583             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19584             this.addItem(r.data);
19585             this.tickItems.push(r.data);
19586         }
19587     },
19588     
19589     getAutoCreateNativeIOS : function()
19590     {
19591         var cfg = {
19592             cls: 'form-group' //input-group,
19593         };
19594         
19595         var combobox =  {
19596             tag: 'select',
19597             cls : 'roo-ios-select'
19598         };
19599         
19600         if (this.name) {
19601             combobox.name = this.name;
19602         }
19603         
19604         if (this.disabled) {
19605             combobox.disabled = true;
19606         }
19607         
19608         var settings = this;
19609         
19610         ['xs','sm','md','lg'].map(function(size){
19611             if (settings[size]) {
19612                 cfg.cls += ' col-' + size + '-' + settings[size];
19613             }
19614         });
19615         
19616         cfg.cn = combobox;
19617         
19618         return cfg;
19619         
19620     },
19621     
19622     initIOSView : function()
19623     {
19624         this.store.on('load', this.onIOSViewLoad, this);
19625         
19626         return;
19627     },
19628     
19629     onIOSViewLoad : function()
19630     {
19631         if(this.store.getCount() < 1){
19632             return;
19633         }
19634         
19635         this.clearIOSView();
19636         
19637         if(this.allowBlank) {
19638             
19639             var default_text = '-- SELECT --';
19640             
19641             if(this.placeholder.length){
19642                 default_text = this.placeholder;
19643             }
19644             
19645             if(this.emptyTitle.length){
19646                 default_text += ' - ' + this.emptyTitle + ' -';
19647             }
19648             
19649             var opt = this.inputEl().createChild({
19650                 tag: 'option',
19651                 value : 0,
19652                 html : default_text
19653             });
19654             
19655             var o = {};
19656             o[this.valueField] = 0;
19657             o[this.displayField] = default_text;
19658             
19659             this.ios_options.push({
19660                 data : o,
19661                 el : opt
19662             });
19663             
19664         }
19665         
19666         this.store.data.each(function(d, rowIndex){
19667             
19668             var html = '';
19669             
19670             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19671                 html = d.data[this.displayField];
19672             }
19673             
19674             var value = '';
19675             
19676             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19677                 value = d.data[this.valueField];
19678             }
19679             
19680             var option = {
19681                 tag: 'option',
19682                 value : value,
19683                 html : html
19684             };
19685             
19686             if(this.value == d.data[this.valueField]){
19687                 option['selected'] = true;
19688             }
19689             
19690             var opt = this.inputEl().createChild(option);
19691             
19692             this.ios_options.push({
19693                 data : d.data,
19694                 el : opt
19695             });
19696             
19697         }, this);
19698         
19699         this.inputEl().on('change', function(){
19700            this.fireEvent('select', this);
19701         }, this);
19702         
19703     },
19704     
19705     clearIOSView: function()
19706     {
19707         this.inputEl().dom.innerHTML = '';
19708         
19709         this.ios_options = [];
19710     },
19711     
19712     setIOSValue: function(v)
19713     {
19714         this.value = v;
19715         
19716         if(!this.ios_options){
19717             return;
19718         }
19719         
19720         Roo.each(this.ios_options, function(opts){
19721            
19722            opts.el.dom.removeAttribute('selected');
19723            
19724            if(opts.data[this.valueField] != v){
19725                return;
19726            }
19727            
19728            opts.el.dom.setAttribute('selected', true);
19729            
19730         }, this);
19731     }
19732
19733     /** 
19734     * @cfg {Boolean} grow 
19735     * @hide 
19736     */
19737     /** 
19738     * @cfg {Number} growMin 
19739     * @hide 
19740     */
19741     /** 
19742     * @cfg {Number} growMax 
19743     * @hide 
19744     */
19745     /**
19746      * @hide
19747      * @method autoSize
19748      */
19749 });
19750
19751 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19752     
19753     header : {
19754         tag: 'div',
19755         cls: 'modal-header',
19756         cn: [
19757             {
19758                 tag: 'h4',
19759                 cls: 'modal-title'
19760             }
19761         ]
19762     },
19763     
19764     body : {
19765         tag: 'div',
19766         cls: 'modal-body',
19767         cn: [
19768             {
19769                 tag: 'ul',
19770                 cls: 'list-group'
19771             }
19772         ]
19773     },
19774     
19775     listItemRadio : {
19776         tag: 'li',
19777         cls: 'list-group-item',
19778         cn: [
19779             {
19780                 tag: 'span',
19781                 cls: 'roo-combobox-list-group-item-value'
19782             },
19783             {
19784                 tag: 'div',
19785                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19786                 cn: [
19787                     {
19788                         tag: 'input',
19789                         type: 'radio'
19790                     },
19791                     {
19792                         tag: 'label'
19793                     }
19794                 ]
19795             }
19796         ]
19797     },
19798     
19799     listItemCheckbox : {
19800         tag: 'li',
19801         cls: 'list-group-item',
19802         cn: [
19803             {
19804                 tag: 'span',
19805                 cls: 'roo-combobox-list-group-item-value'
19806             },
19807             {
19808                 tag: 'div',
19809                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19810                 cn: [
19811                     {
19812                         tag: 'input',
19813                         type: 'checkbox'
19814                     },
19815                     {
19816                         tag: 'label'
19817                     }
19818                 ]
19819             }
19820         ]
19821     },
19822     
19823     emptyResult : {
19824         tag: 'div',
19825         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19826     },
19827     
19828     footer : {
19829         tag: 'div',
19830         cls: 'modal-footer',
19831         cn: [
19832             {
19833                 tag: 'div',
19834                 cls: 'row',
19835                 cn: [
19836                     {
19837                         tag: 'div',
19838                         cls: 'col-xs-6 text-left',
19839                         cn: {
19840                             tag: 'button',
19841                             cls: 'btn btn-danger roo-touch-view-cancel',
19842                             html: 'Cancel'
19843                         }
19844                     },
19845                     {
19846                         tag: 'div',
19847                         cls: 'col-xs-6 text-right',
19848                         cn: {
19849                             tag: 'button',
19850                             cls: 'btn btn-success roo-touch-view-ok',
19851                             html: 'OK'
19852                         }
19853                     }
19854                 ]
19855             }
19856         ]
19857         
19858     }
19859 });
19860
19861 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19862     
19863     touchViewTemplate : {
19864         tag: 'div',
19865         cls: 'modal fade roo-combobox-touch-view',
19866         cn: [
19867             {
19868                 tag: 'div',
19869                 cls: 'modal-dialog',
19870                 style : 'position:fixed', // we have to fix position....
19871                 cn: [
19872                     {
19873                         tag: 'div',
19874                         cls: 'modal-content',
19875                         cn: [
19876                             Roo.bootstrap.form.ComboBox.header,
19877                             Roo.bootstrap.form.ComboBox.body,
19878                             Roo.bootstrap.form.ComboBox.footer
19879                         ]
19880                     }
19881                 ]
19882             }
19883         ]
19884     }
19885 });/*
19886  * Based on:
19887  * Ext JS Library 1.1.1
19888  * Copyright(c) 2006-2007, Ext JS, LLC.
19889  *
19890  * Originally Released Under LGPL - original licence link has changed is not relivant.
19891  *
19892  * Fork - LGPL
19893  * <script type="text/javascript">
19894  */
19895
19896 /**
19897  * @class Roo.View
19898  * @extends Roo.util.Observable
19899  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19900  * This class also supports single and multi selection modes. <br>
19901  * Create a data model bound view:
19902  <pre><code>
19903  var store = new Roo.data.Store(...);
19904
19905  var view = new Roo.View({
19906     el : "my-element",
19907     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19908  
19909     singleSelect: true,
19910     selectedClass: "ydataview-selected",
19911     store: store
19912  });
19913
19914  // listen for node click?
19915  view.on("click", function(vw, index, node, e){
19916  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19917  });
19918
19919  // load XML data
19920  dataModel.load("foobar.xml");
19921  </code></pre>
19922  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19923  * <br><br>
19924  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19925  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19926  * 
19927  * Note: old style constructor is still suported (container, template, config)
19928  * 
19929  * @constructor
19930  * Create a new View
19931  * @param {Object} config The config object
19932  * 
19933  */
19934 Roo.View = function(config, depreciated_tpl, depreciated_config){
19935     
19936     this.parent = false;
19937     
19938     if (typeof(depreciated_tpl) == 'undefined') {
19939         // new way.. - universal constructor.
19940         Roo.apply(this, config);
19941         this.el  = Roo.get(this.el);
19942     } else {
19943         // old format..
19944         this.el  = Roo.get(config);
19945         this.tpl = depreciated_tpl;
19946         Roo.apply(this, depreciated_config);
19947     }
19948     this.wrapEl  = this.el.wrap().wrap();
19949     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19950     
19951     
19952     if(typeof(this.tpl) == "string"){
19953         this.tpl = new Roo.Template(this.tpl);
19954     } else {
19955         // support xtype ctors..
19956         this.tpl = new Roo.factory(this.tpl, Roo);
19957     }
19958     
19959     
19960     this.tpl.compile();
19961     
19962     /** @private */
19963     this.addEvents({
19964         /**
19965          * @event beforeclick
19966          * Fires before a click is processed. Returns false to cancel the default action.
19967          * @param {Roo.View} this
19968          * @param {Number} index The index of the target node
19969          * @param {HTMLElement} node The target node
19970          * @param {Roo.EventObject} e The raw event object
19971          */
19972             "beforeclick" : true,
19973         /**
19974          * @event click
19975          * Fires when a template node is clicked.
19976          * @param {Roo.View} this
19977          * @param {Number} index The index of the target node
19978          * @param {HTMLElement} node The target node
19979          * @param {Roo.EventObject} e The raw event object
19980          */
19981             "click" : true,
19982         /**
19983          * @event dblclick
19984          * Fires when a template node is double clicked.
19985          * @param {Roo.View} this
19986          * @param {Number} index The index of the target node
19987          * @param {HTMLElement} node The target node
19988          * @param {Roo.EventObject} e The raw event object
19989          */
19990             "dblclick" : true,
19991         /**
19992          * @event contextmenu
19993          * Fires when a template node is right clicked.
19994          * @param {Roo.View} this
19995          * @param {Number} index The index of the target node
19996          * @param {HTMLElement} node The target node
19997          * @param {Roo.EventObject} e The raw event object
19998          */
19999             "contextmenu" : true,
20000         /**
20001          * @event selectionchange
20002          * Fires when the selected nodes change.
20003          * @param {Roo.View} this
20004          * @param {Array} selections Array of the selected nodes
20005          */
20006             "selectionchange" : true,
20007     
20008         /**
20009          * @event beforeselect
20010          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20011          * @param {Roo.View} this
20012          * @param {HTMLElement} node The node to be selected
20013          * @param {Array} selections Array of currently selected nodes
20014          */
20015             "beforeselect" : true,
20016         /**
20017          * @event preparedata
20018          * Fires on every row to render, to allow you to change the data.
20019          * @param {Roo.View} this
20020          * @param {Object} data to be rendered (change this)
20021          */
20022           "preparedata" : true
20023           
20024           
20025         });
20026
20027
20028
20029     this.el.on({
20030         "click": this.onClick,
20031         "dblclick": this.onDblClick,
20032         "contextmenu": this.onContextMenu,
20033         scope:this
20034     });
20035
20036     this.selections = [];
20037     this.nodes = [];
20038     this.cmp = new Roo.CompositeElementLite([]);
20039     if(this.store){
20040         this.store = Roo.factory(this.store, Roo.data);
20041         this.setStore(this.store, true);
20042     }
20043     
20044     if ( this.footer && this.footer.xtype) {
20045            
20046          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20047         
20048         this.footer.dataSource = this.store;
20049         this.footer.container = fctr;
20050         this.footer = Roo.factory(this.footer, Roo);
20051         fctr.insertFirst(this.el);
20052         
20053         // this is a bit insane - as the paging toolbar seems to detach the el..
20054 //        dom.parentNode.parentNode.parentNode
20055          // they get detached?
20056     }
20057     
20058     
20059     Roo.View.superclass.constructor.call(this);
20060     
20061     
20062 };
20063
20064 Roo.extend(Roo.View, Roo.util.Observable, {
20065     
20066      /**
20067      * @cfg {Roo.data.Store} store Data store to load data from.
20068      */
20069     store : false,
20070     
20071     /**
20072      * @cfg {String|Roo.Element} el The container element.
20073      */
20074     el : '',
20075     
20076     /**
20077      * @cfg {String|Roo.Template} tpl The template used by this View 
20078      */
20079     tpl : false,
20080     /**
20081      * @cfg {String} dataName the named area of the template to use as the data area
20082      *                          Works with domtemplates roo-name="name"
20083      */
20084     dataName: false,
20085     /**
20086      * @cfg {String} selectedClass The css class to add to selected nodes
20087      */
20088     selectedClass : "x-view-selected",
20089      /**
20090      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20091      */
20092     emptyText : "",
20093     
20094     /**
20095      * @cfg {String} text to display on mask (default Loading)
20096      */
20097     mask : false,
20098     /**
20099      * @cfg {Boolean} multiSelect Allow multiple selection
20100      */
20101     multiSelect : false,
20102     /**
20103      * @cfg {Boolean} singleSelect Allow single selection
20104      */
20105     singleSelect:  false,
20106     
20107     /**
20108      * @cfg {Boolean} toggleSelect - selecting 
20109      */
20110     toggleSelect : false,
20111     
20112     /**
20113      * @cfg {Boolean} tickable - selecting 
20114      */
20115     tickable : false,
20116     
20117     /**
20118      * Returns the element this view is bound to.
20119      * @return {Roo.Element}
20120      */
20121     getEl : function(){
20122         return this.wrapEl;
20123     },
20124     
20125     
20126
20127     /**
20128      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20129      */
20130     refresh : function(){
20131         //Roo.log('refresh');
20132         var t = this.tpl;
20133         
20134         // if we are using something like 'domtemplate', then
20135         // the what gets used is:
20136         // t.applySubtemplate(NAME, data, wrapping data..)
20137         // the outer template then get' applied with
20138         //     the store 'extra data'
20139         // and the body get's added to the
20140         //      roo-name="data" node?
20141         //      <span class='roo-tpl-{name}'></span> ?????
20142         
20143         
20144         
20145         this.clearSelections();
20146         this.el.update("");
20147         var html = [];
20148         var records = this.store.getRange();
20149         if(records.length < 1) {
20150             
20151             // is this valid??  = should it render a template??
20152             
20153             this.el.update(this.emptyText);
20154             return;
20155         }
20156         var el = this.el;
20157         if (this.dataName) {
20158             this.el.update(t.apply(this.store.meta)); //????
20159             el = this.el.child('.roo-tpl-' + this.dataName);
20160         }
20161         
20162         for(var i = 0, len = records.length; i < len; i++){
20163             var data = this.prepareData(records[i].data, i, records[i]);
20164             this.fireEvent("preparedata", this, data, i, records[i]);
20165             
20166             var d = Roo.apply({}, data);
20167             
20168             if(this.tickable){
20169                 Roo.apply(d, {'roo-id' : Roo.id()});
20170                 
20171                 var _this = this;
20172             
20173                 Roo.each(this.parent.item, function(item){
20174                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20175                         return;
20176                     }
20177                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20178                 });
20179             }
20180             
20181             html[html.length] = Roo.util.Format.trim(
20182                 this.dataName ?
20183                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20184                     t.apply(d)
20185             );
20186         }
20187         
20188         
20189         
20190         el.update(html.join(""));
20191         this.nodes = el.dom.childNodes;
20192         this.updateIndexes(0);
20193     },
20194     
20195
20196     /**
20197      * Function to override to reformat the data that is sent to
20198      * the template for each node.
20199      * DEPRICATED - use the preparedata event handler.
20200      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20201      * a JSON object for an UpdateManager bound view).
20202      */
20203     prepareData : function(data, index, record)
20204     {
20205         this.fireEvent("preparedata", this, data, index, record);
20206         return data;
20207     },
20208
20209     onUpdate : function(ds, record){
20210         // Roo.log('on update');   
20211         this.clearSelections();
20212         var index = this.store.indexOf(record);
20213         var n = this.nodes[index];
20214         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20215         n.parentNode.removeChild(n);
20216         this.updateIndexes(index, index);
20217     },
20218
20219     
20220     
20221 // --------- FIXME     
20222     onAdd : function(ds, records, index)
20223     {
20224         //Roo.log(['on Add', ds, records, index] );        
20225         this.clearSelections();
20226         if(this.nodes.length == 0){
20227             this.refresh();
20228             return;
20229         }
20230         var n = this.nodes[index];
20231         for(var i = 0, len = records.length; i < len; i++){
20232             var d = this.prepareData(records[i].data, i, records[i]);
20233             if(n){
20234                 this.tpl.insertBefore(n, d);
20235             }else{
20236                 
20237                 this.tpl.append(this.el, d);
20238             }
20239         }
20240         this.updateIndexes(index);
20241     },
20242
20243     onRemove : function(ds, record, index){
20244        // Roo.log('onRemove');
20245         this.clearSelections();
20246         var el = this.dataName  ?
20247             this.el.child('.roo-tpl-' + this.dataName) :
20248             this.el; 
20249         
20250         el.dom.removeChild(this.nodes[index]);
20251         this.updateIndexes(index);
20252     },
20253
20254     /**
20255      * Refresh an individual node.
20256      * @param {Number} index
20257      */
20258     refreshNode : function(index){
20259         this.onUpdate(this.store, this.store.getAt(index));
20260     },
20261
20262     updateIndexes : function(startIndex, endIndex){
20263         var ns = this.nodes;
20264         startIndex = startIndex || 0;
20265         endIndex = endIndex || ns.length - 1;
20266         for(var i = startIndex; i <= endIndex; i++){
20267             ns[i].nodeIndex = i;
20268         }
20269     },
20270
20271     /**
20272      * Changes the data store this view uses and refresh the view.
20273      * @param {Store} store
20274      */
20275     setStore : function(store, initial){
20276         if(!initial && this.store){
20277             this.store.un("datachanged", this.refresh);
20278             this.store.un("add", this.onAdd);
20279             this.store.un("remove", this.onRemove);
20280             this.store.un("update", this.onUpdate);
20281             this.store.un("clear", this.refresh);
20282             this.store.un("beforeload", this.onBeforeLoad);
20283             this.store.un("load", this.onLoad);
20284             this.store.un("loadexception", this.onLoad);
20285         }
20286         if(store){
20287           
20288             store.on("datachanged", this.refresh, this);
20289             store.on("add", this.onAdd, this);
20290             store.on("remove", this.onRemove, this);
20291             store.on("update", this.onUpdate, this);
20292             store.on("clear", this.refresh, this);
20293             store.on("beforeload", this.onBeforeLoad, this);
20294             store.on("load", this.onLoad, this);
20295             store.on("loadexception", this.onLoad, this);
20296         }
20297         
20298         if(store){
20299             this.refresh();
20300         }
20301     },
20302     /**
20303      * onbeforeLoad - masks the loading area.
20304      *
20305      */
20306     onBeforeLoad : function(store,opts)
20307     {
20308          //Roo.log('onBeforeLoad');   
20309         if (!opts.add) {
20310             this.el.update("");
20311         }
20312         this.el.mask(this.mask ? this.mask : "Loading" ); 
20313     },
20314     onLoad : function ()
20315     {
20316         this.el.unmask();
20317     },
20318     
20319
20320     /**
20321      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20322      * @param {HTMLElement} node
20323      * @return {HTMLElement} The template node
20324      */
20325     findItemFromChild : function(node){
20326         var el = this.dataName  ?
20327             this.el.child('.roo-tpl-' + this.dataName,true) :
20328             this.el.dom; 
20329         
20330         if(!node || node.parentNode == el){
20331                     return node;
20332             }
20333             var p = node.parentNode;
20334             while(p && p != el){
20335             if(p.parentNode == el){
20336                 return p;
20337             }
20338             p = p.parentNode;
20339         }
20340             return null;
20341     },
20342
20343     /** @ignore */
20344     onClick : function(e){
20345         var item = this.findItemFromChild(e.getTarget());
20346         if(item){
20347             var index = this.indexOf(item);
20348             if(this.onItemClick(item, index, e) !== false){
20349                 this.fireEvent("click", this, index, item, e);
20350             }
20351         }else{
20352             this.clearSelections();
20353         }
20354     },
20355
20356     /** @ignore */
20357     onContextMenu : function(e){
20358         var item = this.findItemFromChild(e.getTarget());
20359         if(item){
20360             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20361         }
20362     },
20363
20364     /** @ignore */
20365     onDblClick : function(e){
20366         var item = this.findItemFromChild(e.getTarget());
20367         if(item){
20368             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20369         }
20370     },
20371
20372     onItemClick : function(item, index, e)
20373     {
20374         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20375             return false;
20376         }
20377         if (this.toggleSelect) {
20378             var m = this.isSelected(item) ? 'unselect' : 'select';
20379             //Roo.log(m);
20380             var _t = this;
20381             _t[m](item, true, false);
20382             return true;
20383         }
20384         if(this.multiSelect || this.singleSelect){
20385             if(this.multiSelect && e.shiftKey && this.lastSelection){
20386                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20387             }else{
20388                 this.select(item, this.multiSelect && e.ctrlKey);
20389                 this.lastSelection = item;
20390             }
20391             
20392             if(!this.tickable){
20393                 e.preventDefault();
20394             }
20395             
20396         }
20397         return true;
20398     },
20399
20400     /**
20401      * Get the number of selected nodes.
20402      * @return {Number}
20403      */
20404     getSelectionCount : function(){
20405         return this.selections.length;
20406     },
20407
20408     /**
20409      * Get the currently selected nodes.
20410      * @return {Array} An array of HTMLElements
20411      */
20412     getSelectedNodes : function(){
20413         return this.selections;
20414     },
20415
20416     /**
20417      * Get the indexes of the selected nodes.
20418      * @return {Array}
20419      */
20420     getSelectedIndexes : function(){
20421         var indexes = [], s = this.selections;
20422         for(var i = 0, len = s.length; i < len; i++){
20423             indexes.push(s[i].nodeIndex);
20424         }
20425         return indexes;
20426     },
20427
20428     /**
20429      * Clear all selections
20430      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20431      */
20432     clearSelections : function(suppressEvent){
20433         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20434             this.cmp.elements = this.selections;
20435             this.cmp.removeClass(this.selectedClass);
20436             this.selections = [];
20437             if(!suppressEvent){
20438                 this.fireEvent("selectionchange", this, this.selections);
20439             }
20440         }
20441     },
20442
20443     /**
20444      * Returns true if the passed node is selected
20445      * @param {HTMLElement/Number} node The node or node index
20446      * @return {Boolean}
20447      */
20448     isSelected : function(node){
20449         var s = this.selections;
20450         if(s.length < 1){
20451             return false;
20452         }
20453         node = this.getNode(node);
20454         return s.indexOf(node) !== -1;
20455     },
20456
20457     /**
20458      * Selects nodes.
20459      * @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
20460      * @param {Boolean} keepExisting (optional) true to keep existing selections
20461      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20462      */
20463     select : function(nodeInfo, keepExisting, suppressEvent){
20464         if(nodeInfo instanceof Array){
20465             if(!keepExisting){
20466                 this.clearSelections(true);
20467             }
20468             for(var i = 0, len = nodeInfo.length; i < len; i++){
20469                 this.select(nodeInfo[i], true, true);
20470             }
20471             return;
20472         } 
20473         var node = this.getNode(nodeInfo);
20474         if(!node || this.isSelected(node)){
20475             return; // already selected.
20476         }
20477         if(!keepExisting){
20478             this.clearSelections(true);
20479         }
20480         
20481         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20482             Roo.fly(node).addClass(this.selectedClass);
20483             this.selections.push(node);
20484             if(!suppressEvent){
20485                 this.fireEvent("selectionchange", this, this.selections);
20486             }
20487         }
20488         
20489         
20490     },
20491       /**
20492      * Unselects nodes.
20493      * @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
20494      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20495      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20496      */
20497     unselect : function(nodeInfo, keepExisting, suppressEvent)
20498     {
20499         if(nodeInfo instanceof Array){
20500             Roo.each(this.selections, function(s) {
20501                 this.unselect(s, nodeInfo);
20502             }, this);
20503             return;
20504         }
20505         var node = this.getNode(nodeInfo);
20506         if(!node || !this.isSelected(node)){
20507             //Roo.log("not selected");
20508             return; // not selected.
20509         }
20510         // fireevent???
20511         var ns = [];
20512         Roo.each(this.selections, function(s) {
20513             if (s == node ) {
20514                 Roo.fly(node).removeClass(this.selectedClass);
20515
20516                 return;
20517             }
20518             ns.push(s);
20519         },this);
20520         
20521         this.selections= ns;
20522         this.fireEvent("selectionchange", this, this.selections);
20523     },
20524
20525     /**
20526      * Gets a template node.
20527      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20528      * @return {HTMLElement} The node or null if it wasn't found
20529      */
20530     getNode : function(nodeInfo){
20531         if(typeof nodeInfo == "string"){
20532             return document.getElementById(nodeInfo);
20533         }else if(typeof nodeInfo == "number"){
20534             return this.nodes[nodeInfo];
20535         }
20536         return nodeInfo;
20537     },
20538
20539     /**
20540      * Gets a range template nodes.
20541      * @param {Number} startIndex
20542      * @param {Number} endIndex
20543      * @return {Array} An array of nodes
20544      */
20545     getNodes : function(start, end){
20546         var ns = this.nodes;
20547         start = start || 0;
20548         end = typeof end == "undefined" ? ns.length - 1 : end;
20549         var nodes = [];
20550         if(start <= end){
20551             for(var i = start; i <= end; i++){
20552                 nodes.push(ns[i]);
20553             }
20554         } else{
20555             for(var i = start; i >= end; i--){
20556                 nodes.push(ns[i]);
20557             }
20558         }
20559         return nodes;
20560     },
20561
20562     /**
20563      * Finds the index of the passed node
20564      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20565      * @return {Number} The index of the node or -1
20566      */
20567     indexOf : function(node){
20568         node = this.getNode(node);
20569         if(typeof node.nodeIndex == "number"){
20570             return node.nodeIndex;
20571         }
20572         var ns = this.nodes;
20573         for(var i = 0, len = ns.length; i < len; i++){
20574             if(ns[i] == node){
20575                 return i;
20576             }
20577         }
20578         return -1;
20579     }
20580 });
20581 /*
20582  * - LGPL
20583  *
20584  * based on jquery fullcalendar
20585  * 
20586  */
20587
20588 Roo.bootstrap = Roo.bootstrap || {};
20589 /**
20590  * @class Roo.bootstrap.Calendar
20591  * @extends Roo.bootstrap.Component
20592  * Bootstrap Calendar class
20593  * @cfg {Boolean} loadMask (true|false) default false
20594  * @cfg {Object} header generate the user specific header of the calendar, default false
20595
20596  * @constructor
20597  * Create a new Container
20598  * @param {Object} config The config object
20599  */
20600
20601
20602
20603 Roo.bootstrap.Calendar = function(config){
20604     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20605      this.addEvents({
20606         /**
20607              * @event select
20608              * Fires when a date is selected
20609              * @param {DatePicker} this
20610              * @param {Date} date The selected date
20611              */
20612         'select': true,
20613         /**
20614              * @event monthchange
20615              * Fires when the displayed month changes 
20616              * @param {DatePicker} this
20617              * @param {Date} date The selected month
20618              */
20619         'monthchange': true,
20620         /**
20621              * @event evententer
20622              * Fires when mouse over an event
20623              * @param {Calendar} this
20624              * @param {event} Event
20625              */
20626         'evententer': true,
20627         /**
20628              * @event eventleave
20629              * Fires when the mouse leaves an
20630              * @param {Calendar} this
20631              * @param {event}
20632              */
20633         'eventleave': true,
20634         /**
20635              * @event eventclick
20636              * Fires when the mouse click an
20637              * @param {Calendar} this
20638              * @param {event}
20639              */
20640         'eventclick': true
20641         
20642     });
20643
20644 };
20645
20646 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20647     
20648           /**
20649      * @cfg {Roo.data.Store} store
20650      * The data source for the calendar
20651      */
20652         store : false,
20653      /**
20654      * @cfg {Number} startDay
20655      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20656      */
20657     startDay : 0,
20658     
20659     loadMask : false,
20660     
20661     header : false,
20662       
20663     getAutoCreate : function(){
20664         
20665         
20666         var fc_button = function(name, corner, style, content ) {
20667             return Roo.apply({},{
20668                 tag : 'span',
20669                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20670                          (corner.length ?
20671                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20672                             ''
20673                         ),
20674                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20675                 unselectable: 'on'
20676             });
20677         };
20678         
20679         var header = {};
20680         
20681         if(!this.header){
20682             header = {
20683                 tag : 'table',
20684                 cls : 'fc-header',
20685                 style : 'width:100%',
20686                 cn : [
20687                     {
20688                         tag: 'tr',
20689                         cn : [
20690                             {
20691                                 tag : 'td',
20692                                 cls : 'fc-header-left',
20693                                 cn : [
20694                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20695                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20696                                     { tag: 'span', cls: 'fc-header-space' },
20697                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20698
20699
20700                                 ]
20701                             },
20702
20703                             {
20704                                 tag : 'td',
20705                                 cls : 'fc-header-center',
20706                                 cn : [
20707                                     {
20708                                         tag: 'span',
20709                                         cls: 'fc-header-title',
20710                                         cn : {
20711                                             tag: 'H2',
20712                                             html : 'month / year'
20713                                         }
20714                                     }
20715
20716                                 ]
20717                             },
20718                             {
20719                                 tag : 'td',
20720                                 cls : 'fc-header-right',
20721                                 cn : [
20722                               /*      fc_button('month', 'left', '', 'month' ),
20723                                     fc_button('week', '', '', 'week' ),
20724                                     fc_button('day', 'right', '', 'day' )
20725                                 */    
20726
20727                                 ]
20728                             }
20729
20730                         ]
20731                     }
20732                 ]
20733             };
20734         }
20735         
20736         header = this.header;
20737         
20738        
20739         var cal_heads = function() {
20740             var ret = [];
20741             // fixme - handle this.
20742             
20743             for (var i =0; i < Date.dayNames.length; i++) {
20744                 var d = Date.dayNames[i];
20745                 ret.push({
20746                     tag: 'th',
20747                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20748                     html : d.substring(0,3)
20749                 });
20750                 
20751             }
20752             ret[0].cls += ' fc-first';
20753             ret[6].cls += ' fc-last';
20754             return ret;
20755         };
20756         var cal_cell = function(n) {
20757             return  {
20758                 tag: 'td',
20759                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20760                 cn : [
20761                     {
20762                         cn : [
20763                             {
20764                                 cls: 'fc-day-number',
20765                                 html: 'D'
20766                             },
20767                             {
20768                                 cls: 'fc-day-content',
20769                              
20770                                 cn : [
20771                                      {
20772                                         style: 'position: relative;' // height: 17px;
20773                                     }
20774                                 ]
20775                             }
20776                             
20777                             
20778                         ]
20779                     }
20780                 ]
20781                 
20782             }
20783         };
20784         var cal_rows = function() {
20785             
20786             var ret = [];
20787             for (var r = 0; r < 6; r++) {
20788                 var row= {
20789                     tag : 'tr',
20790                     cls : 'fc-week',
20791                     cn : []
20792                 };
20793                 
20794                 for (var i =0; i < Date.dayNames.length; i++) {
20795                     var d = Date.dayNames[i];
20796                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20797
20798                 }
20799                 row.cn[0].cls+=' fc-first';
20800                 row.cn[0].cn[0].style = 'min-height:90px';
20801                 row.cn[6].cls+=' fc-last';
20802                 ret.push(row);
20803                 
20804             }
20805             ret[0].cls += ' fc-first';
20806             ret[4].cls += ' fc-prev-last';
20807             ret[5].cls += ' fc-last';
20808             return ret;
20809             
20810         };
20811         
20812         var cal_table = {
20813             tag: 'table',
20814             cls: 'fc-border-separate',
20815             style : 'width:100%',
20816             cellspacing  : 0,
20817             cn : [
20818                 { 
20819                     tag: 'thead',
20820                     cn : [
20821                         { 
20822                             tag: 'tr',
20823                             cls : 'fc-first fc-last',
20824                             cn : cal_heads()
20825                         }
20826                     ]
20827                 },
20828                 { 
20829                     tag: 'tbody',
20830                     cn : cal_rows()
20831                 }
20832                   
20833             ]
20834         };
20835          
20836          var cfg = {
20837             cls : 'fc fc-ltr',
20838             cn : [
20839                 header,
20840                 {
20841                     cls : 'fc-content',
20842                     style : "position: relative;",
20843                     cn : [
20844                         {
20845                             cls : 'fc-view fc-view-month fc-grid',
20846                             style : 'position: relative',
20847                             unselectable : 'on',
20848                             cn : [
20849                                 {
20850                                     cls : 'fc-event-container',
20851                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20852                                 },
20853                                 cal_table
20854                             ]
20855                         }
20856                     ]
20857     
20858                 }
20859            ] 
20860             
20861         };
20862         
20863          
20864         
20865         return cfg;
20866     },
20867     
20868     
20869     initEvents : function()
20870     {
20871         if(!this.store){
20872             throw "can not find store for calendar";
20873         }
20874         
20875         var mark = {
20876             tag: "div",
20877             cls:"x-dlg-mask",
20878             style: "text-align:center",
20879             cn: [
20880                 {
20881                     tag: "div",
20882                     style: "background-color:white;width:50%;margin:250 auto",
20883                     cn: [
20884                         {
20885                             tag: "img",
20886                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20887                         },
20888                         {
20889                             tag: "span",
20890                             html: "Loading"
20891                         }
20892                         
20893                     ]
20894                 }
20895             ]
20896         };
20897         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20898         
20899         var size = this.el.select('.fc-content', true).first().getSize();
20900         this.maskEl.setSize(size.width, size.height);
20901         this.maskEl.enableDisplayMode("block");
20902         if(!this.loadMask){
20903             this.maskEl.hide();
20904         }
20905         
20906         this.store = Roo.factory(this.store, Roo.data);
20907         this.store.on('load', this.onLoad, this);
20908         this.store.on('beforeload', this.onBeforeLoad, this);
20909         
20910         this.resize();
20911         
20912         this.cells = this.el.select('.fc-day',true);
20913         //Roo.log(this.cells);
20914         this.textNodes = this.el.query('.fc-day-number');
20915         this.cells.addClassOnOver('fc-state-hover');
20916         
20917         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20918         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20919         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20920         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20921         
20922         this.on('monthchange', this.onMonthChange, this);
20923         
20924         this.update(new Date().clearTime());
20925     },
20926     
20927     resize : function() {
20928         var sz  = this.el.getSize();
20929         
20930         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20931         this.el.select('.fc-day-content div',true).setHeight(34);
20932     },
20933     
20934     
20935     // private
20936     showPrevMonth : function(e){
20937         this.update(this.activeDate.add("mo", -1));
20938     },
20939     showToday : function(e){
20940         this.update(new Date().clearTime());
20941     },
20942     // private
20943     showNextMonth : function(e){
20944         this.update(this.activeDate.add("mo", 1));
20945     },
20946
20947     // private
20948     showPrevYear : function(){
20949         this.update(this.activeDate.add("y", -1));
20950     },
20951
20952     // private
20953     showNextYear : function(){
20954         this.update(this.activeDate.add("y", 1));
20955     },
20956
20957     
20958    // private
20959     update : function(date)
20960     {
20961         var vd = this.activeDate;
20962         this.activeDate = date;
20963 //        if(vd && this.el){
20964 //            var t = date.getTime();
20965 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20966 //                Roo.log('using add remove');
20967 //                
20968 //                this.fireEvent('monthchange', this, date);
20969 //                
20970 //                this.cells.removeClass("fc-state-highlight");
20971 //                this.cells.each(function(c){
20972 //                   if(c.dateValue == t){
20973 //                       c.addClass("fc-state-highlight");
20974 //                       setTimeout(function(){
20975 //                            try{c.dom.firstChild.focus();}catch(e){}
20976 //                       }, 50);
20977 //                       return false;
20978 //                   }
20979 //                   return true;
20980 //                });
20981 //                return;
20982 //            }
20983 //        }
20984         
20985         var days = date.getDaysInMonth();
20986         
20987         var firstOfMonth = date.getFirstDateOfMonth();
20988         var startingPos = firstOfMonth.getDay()-this.startDay;
20989         
20990         if(startingPos < this.startDay){
20991             startingPos += 7;
20992         }
20993         
20994         var pm = date.add(Date.MONTH, -1);
20995         var prevStart = pm.getDaysInMonth()-startingPos;
20996 //        
20997         this.cells = this.el.select('.fc-day',true);
20998         this.textNodes = this.el.query('.fc-day-number');
20999         this.cells.addClassOnOver('fc-state-hover');
21000         
21001         var cells = this.cells.elements;
21002         var textEls = this.textNodes;
21003         
21004         Roo.each(cells, function(cell){
21005             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21006         });
21007         
21008         days += startingPos;
21009
21010         // convert everything to numbers so it's fast
21011         var day = 86400000;
21012         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21013         //Roo.log(d);
21014         //Roo.log(pm);
21015         //Roo.log(prevStart);
21016         
21017         var today = new Date().clearTime().getTime();
21018         var sel = date.clearTime().getTime();
21019         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21020         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21021         var ddMatch = this.disabledDatesRE;
21022         var ddText = this.disabledDatesText;
21023         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21024         var ddaysText = this.disabledDaysText;
21025         var format = this.format;
21026         
21027         var setCellClass = function(cal, cell){
21028             cell.row = 0;
21029             cell.events = [];
21030             cell.more = [];
21031             //Roo.log('set Cell Class');
21032             cell.title = "";
21033             var t = d.getTime();
21034             
21035             //Roo.log(d);
21036             
21037             cell.dateValue = t;
21038             if(t == today){
21039                 cell.className += " fc-today";
21040                 cell.className += " fc-state-highlight";
21041                 cell.title = cal.todayText;
21042             }
21043             if(t == sel){
21044                 // disable highlight in other month..
21045                 //cell.className += " fc-state-highlight";
21046                 
21047             }
21048             // disabling
21049             if(t < min) {
21050                 cell.className = " fc-state-disabled";
21051                 cell.title = cal.minText;
21052                 return;
21053             }
21054             if(t > max) {
21055                 cell.className = " fc-state-disabled";
21056                 cell.title = cal.maxText;
21057                 return;
21058             }
21059             if(ddays){
21060                 if(ddays.indexOf(d.getDay()) != -1){
21061                     cell.title = ddaysText;
21062                     cell.className = " fc-state-disabled";
21063                 }
21064             }
21065             if(ddMatch && format){
21066                 var fvalue = d.dateFormat(format);
21067                 if(ddMatch.test(fvalue)){
21068                     cell.title = ddText.replace("%0", fvalue);
21069                     cell.className = " fc-state-disabled";
21070                 }
21071             }
21072             
21073             if (!cell.initialClassName) {
21074                 cell.initialClassName = cell.dom.className;
21075             }
21076             
21077             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21078         };
21079
21080         var i = 0;
21081         
21082         for(; i < startingPos; i++) {
21083             textEls[i].innerHTML = (++prevStart);
21084             d.setDate(d.getDate()+1);
21085             
21086             cells[i].className = "fc-past fc-other-month";
21087             setCellClass(this, cells[i]);
21088         }
21089         
21090         var intDay = 0;
21091         
21092         for(; i < days; i++){
21093             intDay = i - startingPos + 1;
21094             textEls[i].innerHTML = (intDay);
21095             d.setDate(d.getDate()+1);
21096             
21097             cells[i].className = ''; // "x-date-active";
21098             setCellClass(this, cells[i]);
21099         }
21100         var extraDays = 0;
21101         
21102         for(; i < 42; i++) {
21103             textEls[i].innerHTML = (++extraDays);
21104             d.setDate(d.getDate()+1);
21105             
21106             cells[i].className = "fc-future fc-other-month";
21107             setCellClass(this, cells[i]);
21108         }
21109         
21110         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21111         
21112         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21113         
21114         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21115         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21116         
21117         if(totalRows != 6){
21118             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21119             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21120         }
21121         
21122         this.fireEvent('monthchange', this, date);
21123         
21124         
21125         /*
21126         if(!this.internalRender){
21127             var main = this.el.dom.firstChild;
21128             var w = main.offsetWidth;
21129             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21130             Roo.fly(main).setWidth(w);
21131             this.internalRender = true;
21132             // opera does not respect the auto grow header center column
21133             // then, after it gets a width opera refuses to recalculate
21134             // without a second pass
21135             if(Roo.isOpera && !this.secondPass){
21136                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21137                 this.secondPass = true;
21138                 this.update.defer(10, this, [date]);
21139             }
21140         }
21141         */
21142         
21143     },
21144     
21145     findCell : function(dt) {
21146         dt = dt.clearTime().getTime();
21147         var ret = false;
21148         this.cells.each(function(c){
21149             //Roo.log("check " +c.dateValue + '?=' + dt);
21150             if(c.dateValue == dt){
21151                 ret = c;
21152                 return false;
21153             }
21154             return true;
21155         });
21156         
21157         return ret;
21158     },
21159     
21160     findCells : function(ev) {
21161         var s = ev.start.clone().clearTime().getTime();
21162        // Roo.log(s);
21163         var e= ev.end.clone().clearTime().getTime();
21164        // Roo.log(e);
21165         var ret = [];
21166         this.cells.each(function(c){
21167              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21168             
21169             if(c.dateValue > e){
21170                 return ;
21171             }
21172             if(c.dateValue < s){
21173                 return ;
21174             }
21175             ret.push(c);
21176         });
21177         
21178         return ret;    
21179     },
21180     
21181 //    findBestRow: function(cells)
21182 //    {
21183 //        var ret = 0;
21184 //        
21185 //        for (var i =0 ; i < cells.length;i++) {
21186 //            ret  = Math.max(cells[i].rows || 0,ret);
21187 //        }
21188 //        return ret;
21189 //        
21190 //    },
21191     
21192     
21193     addItem : function(ev)
21194     {
21195         // look for vertical location slot in
21196         var cells = this.findCells(ev);
21197         
21198 //        ev.row = this.findBestRow(cells);
21199         
21200         // work out the location.
21201         
21202         var crow = false;
21203         var rows = [];
21204         for(var i =0; i < cells.length; i++) {
21205             
21206             cells[i].row = cells[0].row;
21207             
21208             if(i == 0){
21209                 cells[i].row = cells[i].row + 1;
21210             }
21211             
21212             if (!crow) {
21213                 crow = {
21214                     start : cells[i],
21215                     end :  cells[i]
21216                 };
21217                 continue;
21218             }
21219             if (crow.start.getY() == cells[i].getY()) {
21220                 // on same row.
21221                 crow.end = cells[i];
21222                 continue;
21223             }
21224             // different row.
21225             rows.push(crow);
21226             crow = {
21227                 start: cells[i],
21228                 end : cells[i]
21229             };
21230             
21231         }
21232         
21233         rows.push(crow);
21234         ev.els = [];
21235         ev.rows = rows;
21236         ev.cells = cells;
21237         
21238         cells[0].events.push(ev);
21239         
21240         this.calevents.push(ev);
21241     },
21242     
21243     clearEvents: function() {
21244         
21245         if(!this.calevents){
21246             return;
21247         }
21248         
21249         Roo.each(this.cells.elements, function(c){
21250             c.row = 0;
21251             c.events = [];
21252             c.more = [];
21253         });
21254         
21255         Roo.each(this.calevents, function(e) {
21256             Roo.each(e.els, function(el) {
21257                 el.un('mouseenter' ,this.onEventEnter, this);
21258                 el.un('mouseleave' ,this.onEventLeave, this);
21259                 el.remove();
21260             },this);
21261         },this);
21262         
21263         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21264             e.remove();
21265         });
21266         
21267     },
21268     
21269     renderEvents: function()
21270     {   
21271         var _this = this;
21272         
21273         this.cells.each(function(c) {
21274             
21275             if(c.row < 5){
21276                 return;
21277             }
21278             
21279             var ev = c.events;
21280             
21281             var r = 4;
21282             if(c.row != c.events.length){
21283                 r = 4 - (4 - (c.row - c.events.length));
21284             }
21285             
21286             c.events = ev.slice(0, r);
21287             c.more = ev.slice(r);
21288             
21289             if(c.more.length && c.more.length == 1){
21290                 c.events.push(c.more.pop());
21291             }
21292             
21293             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21294             
21295         });
21296             
21297         this.cells.each(function(c) {
21298             
21299             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21300             
21301             
21302             for (var e = 0; e < c.events.length; e++){
21303                 var ev = c.events[e];
21304                 var rows = ev.rows;
21305                 
21306                 for(var i = 0; i < rows.length; i++) {
21307                 
21308                     // how many rows should it span..
21309
21310                     var  cfg = {
21311                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21312                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21313
21314                         unselectable : "on",
21315                         cn : [
21316                             {
21317                                 cls: 'fc-event-inner',
21318                                 cn : [
21319     //                                {
21320     //                                  tag:'span',
21321     //                                  cls: 'fc-event-time',
21322     //                                  html : cells.length > 1 ? '' : ev.time
21323     //                                },
21324                                     {
21325                                       tag:'span',
21326                                       cls: 'fc-event-title',
21327                                       html : String.format('{0}', ev.title)
21328                                     }
21329
21330
21331                                 ]
21332                             },
21333                             {
21334                                 cls: 'ui-resizable-handle ui-resizable-e',
21335                                 html : '&nbsp;&nbsp;&nbsp'
21336                             }
21337
21338                         ]
21339                     };
21340
21341                     if (i == 0) {
21342                         cfg.cls += ' fc-event-start';
21343                     }
21344                     if ((i+1) == rows.length) {
21345                         cfg.cls += ' fc-event-end';
21346                     }
21347
21348                     var ctr = _this.el.select('.fc-event-container',true).first();
21349                     var cg = ctr.createChild(cfg);
21350
21351                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21352                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21353
21354                     var r = (c.more.length) ? 1 : 0;
21355                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21356                     cg.setWidth(ebox.right - sbox.x -2);
21357
21358                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21359                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21360                     cg.on('click', _this.onEventClick, _this, ev);
21361
21362                     ev.els.push(cg);
21363                     
21364                 }
21365                 
21366             }
21367             
21368             
21369             if(c.more.length){
21370                 var  cfg = {
21371                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21372                     style : 'position: absolute',
21373                     unselectable : "on",
21374                     cn : [
21375                         {
21376                             cls: 'fc-event-inner',
21377                             cn : [
21378                                 {
21379                                   tag:'span',
21380                                   cls: 'fc-event-title',
21381                                   html : 'More'
21382                                 }
21383
21384
21385                             ]
21386                         },
21387                         {
21388                             cls: 'ui-resizable-handle ui-resizable-e',
21389                             html : '&nbsp;&nbsp;&nbsp'
21390                         }
21391
21392                     ]
21393                 };
21394
21395                 var ctr = _this.el.select('.fc-event-container',true).first();
21396                 var cg = ctr.createChild(cfg);
21397
21398                 var sbox = c.select('.fc-day-content',true).first().getBox();
21399                 var ebox = c.select('.fc-day-content',true).first().getBox();
21400                 //Roo.log(cg);
21401                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21402                 cg.setWidth(ebox.right - sbox.x -2);
21403
21404                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21405                 
21406             }
21407             
21408         });
21409         
21410         
21411         
21412     },
21413     
21414     onEventEnter: function (e, el,event,d) {
21415         this.fireEvent('evententer', this, el, event);
21416     },
21417     
21418     onEventLeave: function (e, el,event,d) {
21419         this.fireEvent('eventleave', this, el, event);
21420     },
21421     
21422     onEventClick: function (e, el,event,d) {
21423         this.fireEvent('eventclick', this, el, event);
21424     },
21425     
21426     onMonthChange: function () {
21427         this.store.load();
21428     },
21429     
21430     onMoreEventClick: function(e, el, more)
21431     {
21432         var _this = this;
21433         
21434         this.calpopover.placement = 'right';
21435         this.calpopover.setTitle('More');
21436         
21437         this.calpopover.setContent('');
21438         
21439         var ctr = this.calpopover.el.select('.popover-content', true).first();
21440         
21441         Roo.each(more, function(m){
21442             var cfg = {
21443                 cls : 'fc-event-hori fc-event-draggable',
21444                 html : m.title
21445             };
21446             var cg = ctr.createChild(cfg);
21447             
21448             cg.on('click', _this.onEventClick, _this, m);
21449         });
21450         
21451         this.calpopover.show(el);
21452         
21453         
21454     },
21455     
21456     onLoad: function () 
21457     {   
21458         this.calevents = [];
21459         var cal = this;
21460         
21461         if(this.store.getCount() > 0){
21462             this.store.data.each(function(d){
21463                cal.addItem({
21464                     id : d.data.id,
21465                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21466                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21467                     time : d.data.start_time,
21468                     title : d.data.title,
21469                     description : d.data.description,
21470                     venue : d.data.venue
21471                 });
21472             });
21473         }
21474         
21475         this.renderEvents();
21476         
21477         if(this.calevents.length && this.loadMask){
21478             this.maskEl.hide();
21479         }
21480     },
21481     
21482     onBeforeLoad: function()
21483     {
21484         this.clearEvents();
21485         if(this.loadMask){
21486             this.maskEl.show();
21487         }
21488     }
21489 });
21490
21491  
21492  /*
21493  * - LGPL
21494  *
21495  * element
21496  * 
21497  */
21498
21499 /**
21500  * @class Roo.bootstrap.Popover
21501  * @extends Roo.bootstrap.Component
21502  * @parent none builder
21503  * @children Roo.bootstrap.Component
21504  * Bootstrap Popover class
21505  * @cfg {String} html contents of the popover   (or false to use children..)
21506  * @cfg {String} title of popover (or false to hide)
21507  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21508  * @cfg {String} trigger click || hover (or false to trigger manually)
21509  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21510  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21511  *      - if false and it has a 'parent' then it will be automatically added to that element
21512  *      - if string - Roo.get  will be called 
21513  * @cfg {Number} delay - delay before showing
21514  
21515  * @constructor
21516  * Create a new Popover
21517  * @param {Object} config The config object
21518  */
21519
21520 Roo.bootstrap.Popover = function(config){
21521     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21522     
21523     this.addEvents({
21524         // raw events
21525          /**
21526          * @event show
21527          * After the popover show
21528          * 
21529          * @param {Roo.bootstrap.Popover} this
21530          */
21531         "show" : true,
21532         /**
21533          * @event hide
21534          * After the popover hide
21535          * 
21536          * @param {Roo.bootstrap.Popover} this
21537          */
21538         "hide" : true
21539     });
21540 };
21541
21542 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21543     
21544     title: false,
21545     html: false,
21546     
21547     placement : 'right',
21548     trigger : 'hover', // hover
21549     modal : false,
21550     delay : 0,
21551     
21552     over: false,
21553     
21554     can_build_overlaid : false,
21555     
21556     maskEl : false, // the mask element
21557     headerEl : false,
21558     contentEl : false,
21559     alignEl : false, // when show is called with an element - this get's stored.
21560     
21561     getChildContainer : function()
21562     {
21563         return this.contentEl;
21564         
21565     },
21566     getPopoverHeader : function()
21567     {
21568         this.title = true; // flag not to hide it..
21569         this.headerEl.addClass('p-0');
21570         return this.headerEl
21571     },
21572     
21573     
21574     getAutoCreate : function(){
21575          
21576         var cfg = {
21577            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21578            style: 'display:block',
21579            cn : [
21580                 {
21581                     cls : 'arrow'
21582                 },
21583                 {
21584                     cls : 'popover-inner ',
21585                     cn : [
21586                         {
21587                             tag: 'h3',
21588                             cls: 'popover-title popover-header',
21589                             html : this.title === false ? '' : this.title
21590                         },
21591                         {
21592                             cls : 'popover-content popover-body '  + (this.cls || ''),
21593                             html : this.html || ''
21594                         }
21595                     ]
21596                     
21597                 }
21598            ]
21599         };
21600         
21601         return cfg;
21602     },
21603     /**
21604      * @param {string} the title
21605      */
21606     setTitle: function(str)
21607     {
21608         this.title = str;
21609         if (this.el) {
21610             this.headerEl.dom.innerHTML = str;
21611         }
21612         
21613     },
21614     /**
21615      * @param {string} the body content
21616      */
21617     setContent: function(str)
21618     {
21619         this.html = str;
21620         if (this.contentEl) {
21621             this.contentEl.dom.innerHTML = str;
21622         }
21623         
21624     },
21625     // as it get's added to the bottom of the page.
21626     onRender : function(ct, position)
21627     {
21628         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21629         
21630         
21631         
21632         if(!this.el){
21633             var cfg = Roo.apply({},  this.getAutoCreate());
21634             cfg.id = Roo.id();
21635             
21636             if (this.cls) {
21637                 cfg.cls += ' ' + this.cls;
21638             }
21639             if (this.style) {
21640                 cfg.style = this.style;
21641             }
21642             //Roo.log("adding to ");
21643             this.el = Roo.get(document.body).createChild(cfg, position);
21644 //            Roo.log(this.el);
21645         }
21646         
21647         this.contentEl = this.el.select('.popover-content',true).first();
21648         this.headerEl =  this.el.select('.popover-title',true).first();
21649         
21650         var nitems = [];
21651         if(typeof(this.items) != 'undefined'){
21652             var items = this.items;
21653             delete this.items;
21654
21655             for(var i =0;i < items.length;i++) {
21656                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21657             }
21658         }
21659
21660         this.items = nitems;
21661         
21662         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21663         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21664         
21665         
21666         
21667         this.initEvents();
21668     },
21669     
21670     resizeMask : function()
21671     {
21672         this.maskEl.setSize(
21673             Roo.lib.Dom.getViewWidth(true),
21674             Roo.lib.Dom.getViewHeight(true)
21675         );
21676     },
21677     
21678     initEvents : function()
21679     {
21680         
21681         if (!this.modal) { 
21682             Roo.bootstrap.Popover.register(this);
21683         }
21684          
21685         this.arrowEl = this.el.select('.arrow',true).first();
21686         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21687         this.el.enableDisplayMode('block');
21688         this.el.hide();
21689  
21690         
21691         if (this.over === false && !this.parent()) {
21692             return; 
21693         }
21694         if (this.triggers === false) {
21695             return;
21696         }
21697          
21698         // support parent
21699         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21700         var triggers = this.trigger ? this.trigger.split(' ') : [];
21701         Roo.each(triggers, function(trigger) {
21702         
21703             if (trigger == 'click') {
21704                 on_el.on('click', this.toggle, this);
21705             } else if (trigger != 'manual') {
21706                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21707                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21708       
21709                 on_el.on(eventIn  ,this.enter, this);
21710                 on_el.on(eventOut, this.leave, this);
21711             }
21712         }, this);
21713     },
21714     
21715     
21716     // private
21717     timeout : null,
21718     hoverState : null,
21719     
21720     toggle : function () {
21721         this.hoverState == 'in' ? this.leave() : this.enter();
21722     },
21723     
21724     enter : function () {
21725         
21726         clearTimeout(this.timeout);
21727     
21728         this.hoverState = 'in';
21729     
21730         if (!this.delay || !this.delay.show) {
21731             this.show();
21732             return;
21733         }
21734         var _t = this;
21735         this.timeout = setTimeout(function () {
21736             if (_t.hoverState == 'in') {
21737                 _t.show();
21738             }
21739         }, this.delay.show)
21740     },
21741     
21742     leave : function() {
21743         clearTimeout(this.timeout);
21744     
21745         this.hoverState = 'out';
21746     
21747         if (!this.delay || !this.delay.hide) {
21748             this.hide();
21749             return;
21750         }
21751         var _t = this;
21752         this.timeout = setTimeout(function () {
21753             if (_t.hoverState == 'out') {
21754                 _t.hide();
21755             }
21756         }, this.delay.hide)
21757     },
21758     
21759     /**
21760      * update the position of the dialog
21761      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21762      * 
21763      *
21764      */
21765     
21766     doAlign : function()
21767     {
21768         
21769         if (this.alignEl) {
21770             this.updatePosition(this.placement, true);
21771              
21772         } else {
21773             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21774             var es = this.el.getSize();
21775             var x = Roo.lib.Dom.getViewWidth()/2;
21776             var y = Roo.lib.Dom.getViewHeight()/2;
21777             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21778             
21779         }
21780
21781          
21782          
21783         
21784         
21785     },
21786     
21787     /**
21788      * Show the popover
21789      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21790      * @param {string} (left|right|top|bottom) position
21791      */
21792     show : function (on_el, placement)
21793     {
21794         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21795         on_el = on_el || false; // default to false
21796          
21797         if (!on_el) {
21798             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21799                 on_el = this.parent().el;
21800             } else if (this.over) {
21801                 on_el = Roo.get(this.over);
21802             }
21803             
21804         }
21805         
21806         this.alignEl = Roo.get( on_el );
21807
21808         if (!this.el) {
21809             this.render(document.body);
21810         }
21811         
21812         
21813          
21814         
21815         if (this.title === false) {
21816             this.headerEl.hide();
21817         }
21818         
21819        
21820         this.el.show();
21821         this.el.dom.style.display = 'block';
21822          
21823         this.doAlign();
21824         
21825         //var arrow = this.el.select('.arrow',true).first();
21826         //arrow.set(align[2], 
21827         
21828         this.el.addClass('in');
21829         
21830          
21831         
21832         this.hoverState = 'in';
21833         
21834         if (this.modal) {
21835             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21836             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21837             this.maskEl.dom.style.display = 'block';
21838             this.maskEl.addClass('show');
21839         }
21840         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21841  
21842         this.fireEvent('show', this);
21843         
21844     },
21845     /**
21846      * fire this manually after loading a grid in the table for example
21847      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21848      * @param {Boolean} try and move it if we cant get right position.
21849      */
21850     updatePosition : function(placement, try_move)
21851     {
21852         // allow for calling with no parameters
21853         placement = placement   ? placement :  this.placement;
21854         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21855         
21856         this.el.removeClass([
21857             'fade','top','bottom', 'left', 'right','in',
21858             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21859         ]);
21860         this.el.addClass(placement + ' bs-popover-' + placement);
21861         
21862         if (!this.alignEl ) {
21863             return false;
21864         }
21865         
21866         switch (placement) {
21867             case 'right':
21868                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21869                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21870                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21871                     //normal display... or moved up/down.
21872                     this.el.setXY(offset);
21873                     var xy = this.alignEl.getAnchorXY('tr', false);
21874                     xy[0]+=2;xy[1]+=5;
21875                     this.arrowEl.setXY(xy);
21876                     return true;
21877                 }
21878                 // continue through...
21879                 return this.updatePosition('left', false);
21880                 
21881             
21882             case 'left':
21883                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21884                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21885                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21886                     //normal display... or moved up/down.
21887                     this.el.setXY(offset);
21888                     var xy = this.alignEl.getAnchorXY('tl', false);
21889                     xy[0]-=10;xy[1]+=5; // << fix me
21890                     this.arrowEl.setXY(xy);
21891                     return true;
21892                 }
21893                 // call self...
21894                 return this.updatePosition('right', false);
21895             
21896             case 'top':
21897                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21898                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21899                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21900                     //normal display... or moved up/down.
21901                     this.el.setXY(offset);
21902                     var xy = this.alignEl.getAnchorXY('t', false);
21903                     xy[1]-=10; // << fix me
21904                     this.arrowEl.setXY(xy);
21905                     return true;
21906                 }
21907                 // fall through
21908                return this.updatePosition('bottom', false);
21909             
21910             case 'bottom':
21911                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21912                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21913                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21914                     //normal display... or moved up/down.
21915                     this.el.setXY(offset);
21916                     var xy = this.alignEl.getAnchorXY('b', false);
21917                      xy[1]+=2; // << fix me
21918                     this.arrowEl.setXY(xy);
21919                     return true;
21920                 }
21921                 // fall through
21922                 return this.updatePosition('top', false);
21923                 
21924             
21925         }
21926         
21927         
21928         return false;
21929     },
21930     
21931     hide : function()
21932     {
21933         this.el.setXY([0,0]);
21934         this.el.removeClass('in');
21935         this.el.hide();
21936         this.hoverState = null;
21937         this.maskEl.hide(); // always..
21938         this.fireEvent('hide', this);
21939     }
21940     
21941 });
21942
21943
21944 Roo.apply(Roo.bootstrap.Popover, {
21945
21946     alignment : {
21947         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21948         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21949         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21950         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21951     },
21952     
21953     zIndex : 20001,
21954
21955     clickHander : false,
21956     
21957     
21958
21959     onMouseDown : function(e)
21960     {
21961         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21962             /// what is nothing is showing..
21963             this.hideAll();
21964         }
21965          
21966     },
21967     
21968     
21969     popups : [],
21970     
21971     register : function(popup)
21972     {
21973         if (!Roo.bootstrap.Popover.clickHandler) {
21974             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21975         }
21976         // hide other popups.
21977         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21978         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21979         this.hideAll(); //<< why?
21980         //this.popups.push(popup);
21981     },
21982     hideAll : function()
21983     {
21984         this.popups.forEach(function(p) {
21985             p.hide();
21986         });
21987     },
21988     onShow : function() {
21989         Roo.bootstrap.Popover.popups.push(this);
21990     },
21991     onHide : function() {
21992         Roo.bootstrap.Popover.popups.remove(this);
21993     } 
21994
21995 });
21996 /**
21997  * @class Roo.bootstrap.PopoverNav
21998  * @extends Roo.bootstrap.nav.Simplebar
21999  * @parent Roo.bootstrap.Popover
22000  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22001  * @licence LGPL
22002  * Bootstrap Popover header navigation class
22003  * FIXME? should this go under nav?
22004  *
22005  * 
22006  * @constructor
22007  * Create a new Popover Header Navigation 
22008  * @param {Object} config The config object
22009  */
22010
22011 Roo.bootstrap.PopoverNav = function(config){
22012     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22013 };
22014
22015 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22016     
22017     
22018     container_method : 'getPopoverHeader' 
22019     
22020      
22021     
22022     
22023    
22024 });
22025
22026  
22027
22028  /*
22029  * - LGPL
22030  *
22031  * Progress
22032  * 
22033  */
22034
22035 /**
22036  * @class Roo.bootstrap.Progress
22037  * @extends Roo.bootstrap.Component
22038  * @children Roo.bootstrap.ProgressBar
22039  * Bootstrap Progress class
22040  * @cfg {Boolean} striped striped of the progress bar
22041  * @cfg {Boolean} active animated of the progress bar
22042  * 
22043  * 
22044  * @constructor
22045  * Create a new Progress
22046  * @param {Object} config The config object
22047  */
22048
22049 Roo.bootstrap.Progress = function(config){
22050     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22051 };
22052
22053 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22054     
22055     striped : false,
22056     active: false,
22057     
22058     getAutoCreate : function(){
22059         var cfg = {
22060             tag: 'div',
22061             cls: 'progress'
22062         };
22063         
22064         
22065         if(this.striped){
22066             cfg.cls += ' progress-striped';
22067         }
22068       
22069         if(this.active){
22070             cfg.cls += ' active';
22071         }
22072         
22073         
22074         return cfg;
22075     }
22076    
22077 });
22078
22079  
22080
22081  /*
22082  * - LGPL
22083  *
22084  * ProgressBar
22085  * 
22086  */
22087
22088 /**
22089  * @class Roo.bootstrap.ProgressBar
22090  * @extends Roo.bootstrap.Component
22091  * Bootstrap ProgressBar class
22092  * @cfg {Number} aria_valuenow aria-value now
22093  * @cfg {Number} aria_valuemin aria-value min
22094  * @cfg {Number} aria_valuemax aria-value max
22095  * @cfg {String} label label for the progress bar
22096  * @cfg {String} panel (success | info | warning | danger )
22097  * @cfg {String} role role of the progress bar
22098  * @cfg {String} sr_only text
22099  * 
22100  * 
22101  * @constructor
22102  * Create a new ProgressBar
22103  * @param {Object} config The config object
22104  */
22105
22106 Roo.bootstrap.ProgressBar = function(config){
22107     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22108 };
22109
22110 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22111     
22112     aria_valuenow : 0,
22113     aria_valuemin : 0,
22114     aria_valuemax : 100,
22115     label : false,
22116     panel : false,
22117     role : false,
22118     sr_only: false,
22119     
22120     getAutoCreate : function()
22121     {
22122         
22123         var cfg = {
22124             tag: 'div',
22125             cls: 'progress-bar',
22126             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22127         };
22128         
22129         if(this.sr_only){
22130             cfg.cn = {
22131                 tag: 'span',
22132                 cls: 'sr-only',
22133                 html: this.sr_only
22134             }
22135         }
22136         
22137         if(this.role){
22138             cfg.role = this.role;
22139         }
22140         
22141         if(this.aria_valuenow){
22142             cfg['aria-valuenow'] = this.aria_valuenow;
22143         }
22144         
22145         if(this.aria_valuemin){
22146             cfg['aria-valuemin'] = this.aria_valuemin;
22147         }
22148         
22149         if(this.aria_valuemax){
22150             cfg['aria-valuemax'] = this.aria_valuemax;
22151         }
22152         
22153         if(this.label && !this.sr_only){
22154             cfg.html = this.label;
22155         }
22156         
22157         if(this.panel){
22158             cfg.cls += ' progress-bar-' + this.panel;
22159         }
22160         
22161         return cfg;
22162     },
22163     
22164     update : function(aria_valuenow)
22165     {
22166         this.aria_valuenow = aria_valuenow;
22167         
22168         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22169     }
22170    
22171 });
22172
22173  
22174
22175  /**
22176  * @class Roo.bootstrap.TabGroup
22177  * @extends Roo.bootstrap.Column
22178  * @children Roo.bootstrap.TabPanel
22179  * Bootstrap Column class
22180  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22181  * @cfg {Boolean} carousel true to make the group behave like a carousel
22182  * @cfg {Boolean} bullets show bullets for the panels
22183  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22184  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22185  * @cfg {Boolean} showarrow (true|false) show arrow default true
22186  * 
22187  * @constructor
22188  * Create a new TabGroup
22189  * @param {Object} config The config object
22190  */
22191
22192 Roo.bootstrap.TabGroup = function(config){
22193     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22194     if (!this.navId) {
22195         this.navId = Roo.id();
22196     }
22197     this.tabs = [];
22198     Roo.bootstrap.TabGroup.register(this);
22199     
22200 };
22201
22202 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22203     
22204     carousel : false,
22205     transition : false,
22206     bullets : 0,
22207     timer : 0,
22208     autoslide : false,
22209     slideFn : false,
22210     slideOnTouch : false,
22211     showarrow : true,
22212     
22213     getAutoCreate : function()
22214     {
22215         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22216         
22217         cfg.cls += ' tab-content';
22218         
22219         if (this.carousel) {
22220             cfg.cls += ' carousel slide';
22221             
22222             cfg.cn = [{
22223                cls : 'carousel-inner',
22224                cn : []
22225             }];
22226         
22227             if(this.bullets  && !Roo.isTouch){
22228                 
22229                 var bullets = {
22230                     cls : 'carousel-bullets',
22231                     cn : []
22232                 };
22233                
22234                 if(this.bullets_cls){
22235                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22236                 }
22237                 
22238                 bullets.cn.push({
22239                     cls : 'clear'
22240                 });
22241                 
22242                 cfg.cn[0].cn.push(bullets);
22243             }
22244             
22245             if(this.showarrow){
22246                 cfg.cn[0].cn.push({
22247                     tag : 'div',
22248                     class : 'carousel-arrow',
22249                     cn : [
22250                         {
22251                             tag : 'div',
22252                             class : 'carousel-prev',
22253                             cn : [
22254                                 {
22255                                     tag : 'i',
22256                                     class : 'fa fa-chevron-left'
22257                                 }
22258                             ]
22259                         },
22260                         {
22261                             tag : 'div',
22262                             class : 'carousel-next',
22263                             cn : [
22264                                 {
22265                                     tag : 'i',
22266                                     class : 'fa fa-chevron-right'
22267                                 }
22268                             ]
22269                         }
22270                     ]
22271                 });
22272             }
22273             
22274         }
22275         
22276         return cfg;
22277     },
22278     
22279     initEvents:  function()
22280     {
22281 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22282 //            this.el.on("touchstart", this.onTouchStart, this);
22283 //        }
22284         
22285         if(this.autoslide){
22286             var _this = this;
22287             
22288             this.slideFn = window.setInterval(function() {
22289                 _this.showPanelNext();
22290             }, this.timer);
22291         }
22292         
22293         if(this.showarrow){
22294             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22295             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22296         }
22297         
22298         
22299     },
22300     
22301 //    onTouchStart : function(e, el, o)
22302 //    {
22303 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22304 //            return;
22305 //        }
22306 //        
22307 //        this.showPanelNext();
22308 //    },
22309     
22310     
22311     getChildContainer : function()
22312     {
22313         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22314     },
22315     
22316     /**
22317     * register a Navigation item
22318     * @param {Roo.bootstrap.nav.Item} the navitem to add
22319     */
22320     register : function(item)
22321     {
22322         this.tabs.push( item);
22323         item.navId = this.navId; // not really needed..
22324         this.addBullet();
22325     
22326     },
22327     
22328     getActivePanel : function()
22329     {
22330         var r = false;
22331         Roo.each(this.tabs, function(t) {
22332             if (t.active) {
22333                 r = t;
22334                 return false;
22335             }
22336             return null;
22337         });
22338         return r;
22339         
22340     },
22341     getPanelByName : function(n)
22342     {
22343         var r = false;
22344         Roo.each(this.tabs, function(t) {
22345             if (t.tabId == n) {
22346                 r = t;
22347                 return false;
22348             }
22349             return null;
22350         });
22351         return r;
22352     },
22353     indexOfPanel : function(p)
22354     {
22355         var r = false;
22356         Roo.each(this.tabs, function(t,i) {
22357             if (t.tabId == p.tabId) {
22358                 r = i;
22359                 return false;
22360             }
22361             return null;
22362         });
22363         return r;
22364     },
22365     /**
22366      * show a specific panel
22367      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22368      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22369      */
22370     showPanel : function (pan)
22371     {
22372         if(this.transition || typeof(pan) == 'undefined'){
22373             Roo.log("waiting for the transitionend");
22374             return false;
22375         }
22376         
22377         if (typeof(pan) == 'number') {
22378             pan = this.tabs[pan];
22379         }
22380         
22381         if (typeof(pan) == 'string') {
22382             pan = this.getPanelByName(pan);
22383         }
22384         
22385         var cur = this.getActivePanel();
22386         
22387         if(!pan || !cur){
22388             Roo.log('pan or acitve pan is undefined');
22389             return false;
22390         }
22391         
22392         if (pan.tabId == this.getActivePanel().tabId) {
22393             return true;
22394         }
22395         
22396         if (false === cur.fireEvent('beforedeactivate')) {
22397             return false;
22398         }
22399         
22400         if(this.bullets > 0 && !Roo.isTouch){
22401             this.setActiveBullet(this.indexOfPanel(pan));
22402         }
22403         
22404         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22405             
22406             //class="carousel-item carousel-item-next carousel-item-left"
22407             
22408             this.transition = true;
22409             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22410             var lr = dir == 'next' ? 'left' : 'right';
22411             pan.el.addClass(dir); // or prev
22412             pan.el.addClass('carousel-item-' + dir); // or prev
22413             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22414             cur.el.addClass(lr); // or right
22415             pan.el.addClass(lr);
22416             cur.el.addClass('carousel-item-' +lr); // or right
22417             pan.el.addClass('carousel-item-' +lr);
22418             
22419             
22420             var _this = this;
22421             cur.el.on('transitionend', function() {
22422                 Roo.log("trans end?");
22423                 
22424                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22425                 pan.setActive(true);
22426                 
22427                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22428                 cur.setActive(false);
22429                 
22430                 _this.transition = false;
22431                 
22432             }, this, { single:  true } );
22433             
22434             return true;
22435         }
22436         
22437         cur.setActive(false);
22438         pan.setActive(true);
22439         
22440         return true;
22441         
22442     },
22443     showPanelNext : function()
22444     {
22445         var i = this.indexOfPanel(this.getActivePanel());
22446         
22447         if (i >= this.tabs.length - 1 && !this.autoslide) {
22448             return;
22449         }
22450         
22451         if (i >= this.tabs.length - 1 && this.autoslide) {
22452             i = -1;
22453         }
22454         
22455         this.showPanel(this.tabs[i+1]);
22456     },
22457     
22458     showPanelPrev : function()
22459     {
22460         var i = this.indexOfPanel(this.getActivePanel());
22461         
22462         if (i  < 1 && !this.autoslide) {
22463             return;
22464         }
22465         
22466         if (i < 1 && this.autoslide) {
22467             i = this.tabs.length;
22468         }
22469         
22470         this.showPanel(this.tabs[i-1]);
22471     },
22472     
22473     
22474     addBullet: function()
22475     {
22476         if(!this.bullets || Roo.isTouch){
22477             return;
22478         }
22479         var ctr = this.el.select('.carousel-bullets',true).first();
22480         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22481         var bullet = ctr.createChild({
22482             cls : 'bullet bullet-' + i
22483         },ctr.dom.lastChild);
22484         
22485         
22486         var _this = this;
22487         
22488         bullet.on('click', (function(e, el, o, ii, t){
22489
22490             e.preventDefault();
22491
22492             this.showPanel(ii);
22493
22494             if(this.autoslide && this.slideFn){
22495                 clearInterval(this.slideFn);
22496                 this.slideFn = window.setInterval(function() {
22497                     _this.showPanelNext();
22498                 }, this.timer);
22499             }
22500
22501         }).createDelegate(this, [i, bullet], true));
22502                 
22503         
22504     },
22505      
22506     setActiveBullet : function(i)
22507     {
22508         if(Roo.isTouch){
22509             return;
22510         }
22511         
22512         Roo.each(this.el.select('.bullet', true).elements, function(el){
22513             el.removeClass('selected');
22514         });
22515
22516         var bullet = this.el.select('.bullet-' + i, true).first();
22517         
22518         if(!bullet){
22519             return;
22520         }
22521         
22522         bullet.addClass('selected');
22523     }
22524     
22525     
22526   
22527 });
22528
22529  
22530
22531  
22532  
22533 Roo.apply(Roo.bootstrap.TabGroup, {
22534     
22535     groups: {},
22536      /**
22537     * register a Navigation Group
22538     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22539     */
22540     register : function(navgrp)
22541     {
22542         this.groups[navgrp.navId] = navgrp;
22543         
22544     },
22545     /**
22546     * fetch a Navigation Group based on the navigation ID
22547     * if one does not exist , it will get created.
22548     * @param {string} the navgroup to add
22549     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22550     */
22551     get: function(navId) {
22552         if (typeof(this.groups[navId]) == 'undefined') {
22553             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22554         }
22555         return this.groups[navId] ;
22556     }
22557     
22558     
22559     
22560 });
22561
22562  /*
22563  * - LGPL
22564  *
22565  * TabPanel
22566  * 
22567  */
22568
22569 /**
22570  * @class Roo.bootstrap.TabPanel
22571  * @extends Roo.bootstrap.Component
22572  * @children Roo.bootstrap.Component
22573  * Bootstrap TabPanel class
22574  * @cfg {Boolean} active panel active
22575  * @cfg {String} html panel content
22576  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22577  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22578  * @cfg {String} href click to link..
22579  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22580  * 
22581  * 
22582  * @constructor
22583  * Create a new TabPanel
22584  * @param {Object} config The config object
22585  */
22586
22587 Roo.bootstrap.TabPanel = function(config){
22588     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22589     this.addEvents({
22590         /**
22591              * @event changed
22592              * Fires when the active status changes
22593              * @param {Roo.bootstrap.TabPanel} this
22594              * @param {Boolean} state the new state
22595             
22596          */
22597         'changed': true,
22598         /**
22599              * @event beforedeactivate
22600              * Fires before a tab is de-activated - can be used to do validation on a form.
22601              * @param {Roo.bootstrap.TabPanel} this
22602              * @return {Boolean} false if there is an error
22603             
22604          */
22605         'beforedeactivate': true
22606      });
22607     
22608     this.tabId = this.tabId || Roo.id();
22609   
22610 };
22611
22612 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22613     
22614     active: false,
22615     html: false,
22616     tabId: false,
22617     navId : false,
22618     href : '',
22619     touchSlide : false,
22620     getAutoCreate : function(){
22621         
22622         
22623         var cfg = {
22624             tag: 'div',
22625             // item is needed for carousel - not sure if it has any effect otherwise
22626             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22627             html: this.html || ''
22628         };
22629         
22630         if(this.active){
22631             cfg.cls += ' active';
22632         }
22633         
22634         if(this.tabId){
22635             cfg.tabId = this.tabId;
22636         }
22637         
22638         
22639         
22640         return cfg;
22641     },
22642     
22643     initEvents:  function()
22644     {
22645         var p = this.parent();
22646         
22647         this.navId = this.navId || p.navId;
22648         
22649         if (typeof(this.navId) != 'undefined') {
22650             // not really needed.. but just in case.. parent should be a NavGroup.
22651             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22652             
22653             tg.register(this);
22654             
22655             var i = tg.tabs.length - 1;
22656             
22657             if(this.active && tg.bullets > 0 && i < tg.bullets){
22658                 tg.setActiveBullet(i);
22659             }
22660         }
22661         
22662         this.el.on('click', this.onClick, this);
22663         
22664         if(Roo.isTouch && this.touchSlide){
22665             this.el.on("touchstart", this.onTouchStart, this);
22666             this.el.on("touchmove", this.onTouchMove, this);
22667             this.el.on("touchend", this.onTouchEnd, this);
22668         }
22669         
22670     },
22671     
22672     onRender : function(ct, position)
22673     {
22674         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22675     },
22676     
22677     setActive : function(state)
22678     {
22679         Roo.log("panel - set active " + this.tabId + "=" + state);
22680         
22681         this.active = state;
22682         if (!state) {
22683             this.el.removeClass('active');
22684             
22685         } else  if (!this.el.hasClass('active')) {
22686             this.el.addClass('active');
22687         }
22688         
22689         this.fireEvent('changed', this, state);
22690     },
22691     
22692     onClick : function(e)
22693     {
22694         e.preventDefault();
22695         
22696         if(!this.href.length){
22697             return;
22698         }
22699         
22700         window.location.href = this.href;
22701     },
22702     
22703     startX : 0,
22704     startY : 0,
22705     endX : 0,
22706     endY : 0,
22707     swiping : false,
22708     
22709     onTouchStart : function(e)
22710     {
22711         this.swiping = false;
22712         
22713         this.startX = e.browserEvent.touches[0].clientX;
22714         this.startY = e.browserEvent.touches[0].clientY;
22715     },
22716     
22717     onTouchMove : function(e)
22718     {
22719         this.swiping = true;
22720         
22721         this.endX = e.browserEvent.touches[0].clientX;
22722         this.endY = e.browserEvent.touches[0].clientY;
22723     },
22724     
22725     onTouchEnd : function(e)
22726     {
22727         if(!this.swiping){
22728             this.onClick(e);
22729             return;
22730         }
22731         
22732         var tabGroup = this.parent();
22733         
22734         if(this.endX > this.startX){ // swiping right
22735             tabGroup.showPanelPrev();
22736             return;
22737         }
22738         
22739         if(this.startX > this.endX){ // swiping left
22740             tabGroup.showPanelNext();
22741             return;
22742         }
22743     }
22744     
22745     
22746 });
22747  
22748
22749  
22750
22751  /*
22752  * - LGPL
22753  *
22754  * DateField
22755  * 
22756  */
22757
22758 /**
22759  * @class Roo.bootstrap.form.DateField
22760  * @extends Roo.bootstrap.form.Input
22761  * Bootstrap DateField class
22762  * @cfg {Number} weekStart default 0
22763  * @cfg {String} viewMode default empty, (months|years)
22764  * @cfg {String} minViewMode default empty, (months|years)
22765  * @cfg {Number} startDate default -Infinity
22766  * @cfg {Number} endDate default Infinity
22767  * @cfg {Boolean} todayHighlight default false
22768  * @cfg {Boolean} todayBtn default false
22769  * @cfg {Boolean} calendarWeeks default false
22770  * @cfg {Object} daysOfWeekDisabled default empty
22771  * @cfg {Boolean} singleMode default false (true | false)
22772  * 
22773  * @cfg {Boolean} keyboardNavigation default true
22774  * @cfg {String} language default en
22775  * 
22776  * @constructor
22777  * Create a new DateField
22778  * @param {Object} config The config object
22779  */
22780
22781 Roo.bootstrap.form.DateField = function(config){
22782     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22783      this.addEvents({
22784             /**
22785              * @event show
22786              * Fires when this field show.
22787              * @param {Roo.bootstrap.form.DateField} this
22788              * @param {Mixed} date The date value
22789              */
22790             show : true,
22791             /**
22792              * @event show
22793              * Fires when this field hide.
22794              * @param {Roo.bootstrap.form.DateField} this
22795              * @param {Mixed} date The date value
22796              */
22797             hide : true,
22798             /**
22799              * @event select
22800              * Fires when select a date.
22801              * @param {Roo.bootstrap.form.DateField} this
22802              * @param {Mixed} date The date value
22803              */
22804             select : true,
22805             /**
22806              * @event beforeselect
22807              * Fires when before select a date.
22808              * @param {Roo.bootstrap.form.DateField} this
22809              * @param {Mixed} date The date value
22810              */
22811             beforeselect : true
22812         });
22813 };
22814
22815 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22816     
22817     /**
22818      * @cfg {String} format
22819      * The default date format string which can be overriden for localization support.  The format must be
22820      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22821      */
22822     format : "m/d/y",
22823     /**
22824      * @cfg {String} altFormats
22825      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22826      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22827      */
22828     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22829     
22830     weekStart : 0,
22831     
22832     viewMode : '',
22833     
22834     minViewMode : '',
22835     
22836     todayHighlight : false,
22837     
22838     todayBtn: false,
22839     
22840     language: 'en',
22841     
22842     keyboardNavigation: true,
22843     
22844     calendarWeeks: false,
22845     
22846     startDate: -Infinity,
22847     
22848     endDate: Infinity,
22849     
22850     daysOfWeekDisabled: [],
22851     
22852     _events: [],
22853     
22854     singleMode : false,
22855     
22856     UTCDate: function()
22857     {
22858         return new Date(Date.UTC.apply(Date, arguments));
22859     },
22860     
22861     UTCToday: function()
22862     {
22863         var today = new Date();
22864         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22865     },
22866     
22867     getDate: function() {
22868             var d = this.getUTCDate();
22869             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22870     },
22871     
22872     getUTCDate: function() {
22873             return this.date;
22874     },
22875     
22876     setDate: function(d) {
22877             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22878     },
22879     
22880     setUTCDate: function(d) {
22881             this.date = d;
22882             this.setValue(this.formatDate(this.date));
22883     },
22884         
22885     onRender: function(ct, position)
22886     {
22887         
22888         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22889         
22890         this.language = this.language || 'en';
22891         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22892         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22893         
22894         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22895         this.format = this.format || 'm/d/y';
22896         this.isInline = false;
22897         this.isInput = true;
22898         this.component = this.el.select('.add-on', true).first() || false;
22899         this.component = (this.component && this.component.length === 0) ? false : this.component;
22900         this.hasInput = this.component && this.inputEl().length;
22901         
22902         if (typeof(this.minViewMode === 'string')) {
22903             switch (this.minViewMode) {
22904                 case 'months':
22905                     this.minViewMode = 1;
22906                     break;
22907                 case 'years':
22908                     this.minViewMode = 2;
22909                     break;
22910                 default:
22911                     this.minViewMode = 0;
22912                     break;
22913             }
22914         }
22915         
22916         if (typeof(this.viewMode === 'string')) {
22917             switch (this.viewMode) {
22918                 case 'months':
22919                     this.viewMode = 1;
22920                     break;
22921                 case 'years':
22922                     this.viewMode = 2;
22923                     break;
22924                 default:
22925                     this.viewMode = 0;
22926                     break;
22927             }
22928         }
22929                 
22930         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22931         
22932 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22933         
22934         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22935         
22936         this.picker().on('mousedown', this.onMousedown, this);
22937         this.picker().on('click', this.onClick, this);
22938         
22939         this.picker().addClass('datepicker-dropdown');
22940         
22941         this.startViewMode = this.viewMode;
22942         
22943         if(this.singleMode){
22944             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22945                 v.setVisibilityMode(Roo.Element.DISPLAY);
22946                 v.hide();
22947             });
22948             
22949             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22950                 v.setStyle('width', '189px');
22951             });
22952         }
22953         
22954         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22955             if(!this.calendarWeeks){
22956                 v.remove();
22957                 return;
22958             }
22959             
22960             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22961             v.attr('colspan', function(i, val){
22962                 return parseInt(val) + 1;
22963             });
22964         });
22965                         
22966         
22967         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22968         
22969         this.setStartDate(this.startDate);
22970         this.setEndDate(this.endDate);
22971         
22972         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22973         
22974         this.fillDow();
22975         this.fillMonths();
22976         this.update();
22977         this.showMode();
22978         
22979         if(this.isInline) {
22980             this.showPopup();
22981         }
22982     },
22983     
22984     picker : function()
22985     {
22986         return this.pickerEl;
22987 //        return this.el.select('.datepicker', true).first();
22988     },
22989     
22990     fillDow: function()
22991     {
22992         var dowCnt = this.weekStart;
22993         
22994         var dow = {
22995             tag: 'tr',
22996             cn: [
22997                 
22998             ]
22999         };
23000         
23001         if(this.calendarWeeks){
23002             dow.cn.push({
23003                 tag: 'th',
23004                 cls: 'cw',
23005                 html: '&nbsp;'
23006             })
23007         }
23008         
23009         while (dowCnt < this.weekStart + 7) {
23010             dow.cn.push({
23011                 tag: 'th',
23012                 cls: 'dow',
23013                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23014             });
23015         }
23016         
23017         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23018     },
23019     
23020     fillMonths: function()
23021     {    
23022         var i = 0;
23023         var months = this.picker().select('>.datepicker-months td', true).first();
23024         
23025         months.dom.innerHTML = '';
23026         
23027         while (i < 12) {
23028             var month = {
23029                 tag: 'span',
23030                 cls: 'month',
23031                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23032             };
23033             
23034             months.createChild(month);
23035         }
23036         
23037     },
23038     
23039     update: function()
23040     {
23041         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;
23042         
23043         if (this.date < this.startDate) {
23044             this.viewDate = new Date(this.startDate);
23045         } else if (this.date > this.endDate) {
23046             this.viewDate = new Date(this.endDate);
23047         } else {
23048             this.viewDate = new Date(this.date);
23049         }
23050         
23051         this.fill();
23052     },
23053     
23054     fill: function() 
23055     {
23056         var d = new Date(this.viewDate),
23057                 year = d.getUTCFullYear(),
23058                 month = d.getUTCMonth(),
23059                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23060                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23061                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23062                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23063                 currentDate = this.date && this.date.valueOf(),
23064                 today = this.UTCToday();
23065         
23066         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23067         
23068 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23069         
23070 //        this.picker.select('>tfoot th.today').
23071 //                                              .text(dates[this.language].today)
23072 //                                              .toggle(this.todayBtn !== false);
23073     
23074         this.updateNavArrows();
23075         this.fillMonths();
23076                                                 
23077         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23078         
23079         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23080          
23081         prevMonth.setUTCDate(day);
23082         
23083         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23084         
23085         var nextMonth = new Date(prevMonth);
23086         
23087         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23088         
23089         nextMonth = nextMonth.valueOf();
23090         
23091         var fillMonths = false;
23092         
23093         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23094         
23095         while(prevMonth.valueOf() <= nextMonth) {
23096             var clsName = '';
23097             
23098             if (prevMonth.getUTCDay() === this.weekStart) {
23099                 if(fillMonths){
23100                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23101                 }
23102                     
23103                 fillMonths = {
23104                     tag: 'tr',
23105                     cn: []
23106                 };
23107                 
23108                 if(this.calendarWeeks){
23109                     // ISO 8601: First week contains first thursday.
23110                     // ISO also states week starts on Monday, but we can be more abstract here.
23111                     var
23112                     // Start of current week: based on weekstart/current date
23113                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23114                     // Thursday of this week
23115                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23116                     // First Thursday of year, year from thursday
23117                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23118                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23119                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23120                     
23121                     fillMonths.cn.push({
23122                         tag: 'td',
23123                         cls: 'cw',
23124                         html: calWeek
23125                     });
23126                 }
23127             }
23128             
23129             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23130                 clsName += ' old';
23131             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23132                 clsName += ' new';
23133             }
23134             if (this.todayHighlight &&
23135                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23136                 prevMonth.getUTCMonth() == today.getMonth() &&
23137                 prevMonth.getUTCDate() == today.getDate()) {
23138                 clsName += ' today';
23139             }
23140             
23141             if (currentDate && prevMonth.valueOf() === currentDate) {
23142                 clsName += ' active';
23143             }
23144             
23145             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23146                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23147                     clsName += ' disabled';
23148             }
23149             
23150             fillMonths.cn.push({
23151                 tag: 'td',
23152                 cls: 'day ' + clsName,
23153                 html: prevMonth.getDate()
23154             });
23155             
23156             prevMonth.setDate(prevMonth.getDate()+1);
23157         }
23158           
23159         var currentYear = this.date && this.date.getUTCFullYear();
23160         var currentMonth = this.date && this.date.getUTCMonth();
23161         
23162         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23163         
23164         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23165             v.removeClass('active');
23166             
23167             if(currentYear === year && k === currentMonth){
23168                 v.addClass('active');
23169             }
23170             
23171             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23172                 v.addClass('disabled');
23173             }
23174             
23175         });
23176         
23177         
23178         year = parseInt(year/10, 10) * 10;
23179         
23180         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23181         
23182         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23183         
23184         year -= 1;
23185         for (var i = -1; i < 11; i++) {
23186             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23187                 tag: 'span',
23188                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23189                 html: year
23190             });
23191             
23192             year += 1;
23193         }
23194     },
23195     
23196     showMode: function(dir) 
23197     {
23198         if (dir) {
23199             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23200         }
23201         
23202         Roo.each(this.picker().select('>div',true).elements, function(v){
23203             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23204             v.hide();
23205         });
23206         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23207     },
23208     
23209     place: function()
23210     {
23211         if(this.isInline) {
23212             return;
23213         }
23214         
23215         this.picker().removeClass(['bottom', 'top']);
23216         
23217         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23218             /*
23219              * place to the top of element!
23220              *
23221              */
23222             
23223             this.picker().addClass('top');
23224             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23225             
23226             return;
23227         }
23228         
23229         this.picker().addClass('bottom');
23230         
23231         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23232     },
23233     
23234     parseDate : function(value)
23235     {
23236         if(!value || value instanceof Date){
23237             return value;
23238         }
23239         var v = Date.parseDate(value, this.format);
23240         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23241             v = Date.parseDate(value, 'Y-m-d');
23242         }
23243         if(!v && this.altFormats){
23244             if(!this.altFormatsArray){
23245                 this.altFormatsArray = this.altFormats.split("|");
23246             }
23247             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23248                 v = Date.parseDate(value, this.altFormatsArray[i]);
23249             }
23250         }
23251         return v;
23252     },
23253     
23254     formatDate : function(date, fmt)
23255     {   
23256         return (!date || !(date instanceof Date)) ?
23257         date : date.dateFormat(fmt || this.format);
23258     },
23259     
23260     onFocus : function()
23261     {
23262         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23263         this.showPopup();
23264     },
23265     
23266     onBlur : function()
23267     {
23268         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23269         
23270         var d = this.inputEl().getValue();
23271         
23272         this.setValue(d);
23273                 
23274         this.hidePopup();
23275     },
23276     
23277     showPopup : function()
23278     {
23279         this.picker().show();
23280         this.update();
23281         this.place();
23282         
23283         this.fireEvent('showpopup', this, this.date);
23284     },
23285     
23286     hidePopup : function()
23287     {
23288         if(this.isInline) {
23289             return;
23290         }
23291         this.picker().hide();
23292         this.viewMode = this.startViewMode;
23293         this.showMode();
23294         
23295         this.fireEvent('hidepopup', this, this.date);
23296         
23297     },
23298     
23299     onMousedown: function(e)
23300     {
23301         e.stopPropagation();
23302         e.preventDefault();
23303     },
23304     
23305     keyup: function(e)
23306     {
23307         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23308         this.update();
23309     },
23310
23311     setValue: function(v)
23312     {
23313         if(this.fireEvent('beforeselect', this, v) !== false){
23314             var d = new Date(this.parseDate(v) ).clearTime();
23315         
23316             if(isNaN(d.getTime())){
23317                 this.date = this.viewDate = '';
23318                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23319                 return;
23320             }
23321
23322             v = this.formatDate(d);
23323
23324             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23325
23326             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23327
23328             this.update();
23329
23330             this.fireEvent('select', this, this.date);
23331         }
23332     },
23333     
23334     getValue: function()
23335     {
23336         return this.formatDate(this.date);
23337     },
23338     
23339     fireKey: function(e)
23340     {
23341         if (!this.picker().isVisible()){
23342             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23343                 this.showPopup();
23344             }
23345             return;
23346         }
23347         
23348         var dateChanged = false,
23349         dir, day, month,
23350         newDate, newViewDate;
23351         
23352         switch(e.keyCode){
23353             case 27: // escape
23354                 this.hidePopup();
23355                 e.preventDefault();
23356                 break;
23357             case 37: // left
23358             case 39: // right
23359                 if (!this.keyboardNavigation) {
23360                     break;
23361                 }
23362                 dir = e.keyCode == 37 ? -1 : 1;
23363                 
23364                 if (e.ctrlKey){
23365                     newDate = this.moveYear(this.date, dir);
23366                     newViewDate = this.moveYear(this.viewDate, dir);
23367                 } else if (e.shiftKey){
23368                     newDate = this.moveMonth(this.date, dir);
23369                     newViewDate = this.moveMonth(this.viewDate, dir);
23370                 } else {
23371                     newDate = new Date(this.date);
23372                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23373                     newViewDate = new Date(this.viewDate);
23374                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23375                 }
23376                 if (this.dateWithinRange(newDate)){
23377                     this.date = newDate;
23378                     this.viewDate = newViewDate;
23379                     this.setValue(this.formatDate(this.date));
23380 //                    this.update();
23381                     e.preventDefault();
23382                     dateChanged = true;
23383                 }
23384                 break;
23385             case 38: // up
23386             case 40: // down
23387                 if (!this.keyboardNavigation) {
23388                     break;
23389                 }
23390                 dir = e.keyCode == 38 ? -1 : 1;
23391                 if (e.ctrlKey){
23392                     newDate = this.moveYear(this.date, dir);
23393                     newViewDate = this.moveYear(this.viewDate, dir);
23394                 } else if (e.shiftKey){
23395                     newDate = this.moveMonth(this.date, dir);
23396                     newViewDate = this.moveMonth(this.viewDate, dir);
23397                 } else {
23398                     newDate = new Date(this.date);
23399                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23400                     newViewDate = new Date(this.viewDate);
23401                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23402                 }
23403                 if (this.dateWithinRange(newDate)){
23404                     this.date = newDate;
23405                     this.viewDate = newViewDate;
23406                     this.setValue(this.formatDate(this.date));
23407 //                    this.update();
23408                     e.preventDefault();
23409                     dateChanged = true;
23410                 }
23411                 break;
23412             case 13: // enter
23413                 this.setValue(this.formatDate(this.date));
23414                 this.hidePopup();
23415                 e.preventDefault();
23416                 break;
23417             case 9: // tab
23418                 this.setValue(this.formatDate(this.date));
23419                 this.hidePopup();
23420                 break;
23421             case 16: // shift
23422             case 17: // ctrl
23423             case 18: // alt
23424                 break;
23425             default :
23426                 this.hidePopup();
23427                 
23428         }
23429     },
23430     
23431     
23432     onClick: function(e) 
23433     {
23434         e.stopPropagation();
23435         e.preventDefault();
23436         
23437         var target = e.getTarget();
23438         
23439         if(target.nodeName.toLowerCase() === 'i'){
23440             target = Roo.get(target).dom.parentNode;
23441         }
23442         
23443         var nodeName = target.nodeName;
23444         var className = target.className;
23445         var html = target.innerHTML;
23446         //Roo.log(nodeName);
23447         
23448         switch(nodeName.toLowerCase()) {
23449             case 'th':
23450                 switch(className) {
23451                     case 'switch':
23452                         this.showMode(1);
23453                         break;
23454                     case 'prev':
23455                     case 'next':
23456                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23457                         switch(this.viewMode){
23458                                 case 0:
23459                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23460                                         break;
23461                                 case 1:
23462                                 case 2:
23463                                         this.viewDate = this.moveYear(this.viewDate, dir);
23464                                         break;
23465                         }
23466                         this.fill();
23467                         break;
23468                     case 'today':
23469                         var date = new Date();
23470                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23471 //                        this.fill()
23472                         this.setValue(this.formatDate(this.date));
23473                         
23474                         this.hidePopup();
23475                         break;
23476                 }
23477                 break;
23478             case 'span':
23479                 if (className.indexOf('disabled') < 0) {
23480                 if (!this.viewDate) {
23481                     this.viewDate = new Date();
23482                 }
23483                 this.viewDate.setUTCDate(1);
23484                     if (className.indexOf('month') > -1) {
23485                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23486                     } else {
23487                         var year = parseInt(html, 10) || 0;
23488                         this.viewDate.setUTCFullYear(year);
23489                         
23490                     }
23491                     
23492                     if(this.singleMode){
23493                         this.setValue(this.formatDate(this.viewDate));
23494                         this.hidePopup();
23495                         return;
23496                     }
23497                     
23498                     this.showMode(-1);
23499                     this.fill();
23500                 }
23501                 break;
23502                 
23503             case 'td':
23504                 //Roo.log(className);
23505                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23506                     var day = parseInt(html, 10) || 1;
23507                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23508                         month = (this.viewDate || new Date()).getUTCMonth();
23509
23510                     if (className.indexOf('old') > -1) {
23511                         if(month === 0 ){
23512                             month = 11;
23513                             year -= 1;
23514                         }else{
23515                             month -= 1;
23516                         }
23517                     } else if (className.indexOf('new') > -1) {
23518                         if (month == 11) {
23519                             month = 0;
23520                             year += 1;
23521                         } else {
23522                             month += 1;
23523                         }
23524                     }
23525                     //Roo.log([year,month,day]);
23526                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23527                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23528 //                    this.fill();
23529                     //Roo.log(this.formatDate(this.date));
23530                     this.setValue(this.formatDate(this.date));
23531                     this.hidePopup();
23532                 }
23533                 break;
23534         }
23535     },
23536     
23537     setStartDate: function(startDate)
23538     {
23539         this.startDate = startDate || -Infinity;
23540         if (this.startDate !== -Infinity) {
23541             this.startDate = this.parseDate(this.startDate);
23542         }
23543         this.update();
23544         this.updateNavArrows();
23545     },
23546
23547     setEndDate: function(endDate)
23548     {
23549         this.endDate = endDate || Infinity;
23550         if (this.endDate !== Infinity) {
23551             this.endDate = this.parseDate(this.endDate);
23552         }
23553         this.update();
23554         this.updateNavArrows();
23555     },
23556     
23557     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23558     {
23559         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23560         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23561             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23562         }
23563         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23564             return parseInt(d, 10);
23565         });
23566         this.update();
23567         this.updateNavArrows();
23568     },
23569     
23570     updateNavArrows: function() 
23571     {
23572         if(this.singleMode){
23573             return;
23574         }
23575         
23576         var d = new Date(this.viewDate),
23577         year = d.getUTCFullYear(),
23578         month = d.getUTCMonth();
23579         
23580         Roo.each(this.picker().select('.prev', true).elements, function(v){
23581             v.show();
23582             switch (this.viewMode) {
23583                 case 0:
23584
23585                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23586                         v.hide();
23587                     }
23588                     break;
23589                 case 1:
23590                 case 2:
23591                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23592                         v.hide();
23593                     }
23594                     break;
23595             }
23596         });
23597         
23598         Roo.each(this.picker().select('.next', true).elements, function(v){
23599             v.show();
23600             switch (this.viewMode) {
23601                 case 0:
23602
23603                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23604                         v.hide();
23605                     }
23606                     break;
23607                 case 1:
23608                 case 2:
23609                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23610                         v.hide();
23611                     }
23612                     break;
23613             }
23614         })
23615     },
23616     
23617     moveMonth: function(date, dir)
23618     {
23619         if (!dir) {
23620             return date;
23621         }
23622         var new_date = new Date(date.valueOf()),
23623         day = new_date.getUTCDate(),
23624         month = new_date.getUTCMonth(),
23625         mag = Math.abs(dir),
23626         new_month, test;
23627         dir = dir > 0 ? 1 : -1;
23628         if (mag == 1){
23629             test = dir == -1
23630             // If going back one month, make sure month is not current month
23631             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23632             ? function(){
23633                 return new_date.getUTCMonth() == month;
23634             }
23635             // If going forward one month, make sure month is as expected
23636             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23637             : function(){
23638                 return new_date.getUTCMonth() != new_month;
23639             };
23640             new_month = month + dir;
23641             new_date.setUTCMonth(new_month);
23642             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23643             if (new_month < 0 || new_month > 11) {
23644                 new_month = (new_month + 12) % 12;
23645             }
23646         } else {
23647             // For magnitudes >1, move one month at a time...
23648             for (var i=0; i<mag; i++) {
23649                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23650                 new_date = this.moveMonth(new_date, dir);
23651             }
23652             // ...then reset the day, keeping it in the new month
23653             new_month = new_date.getUTCMonth();
23654             new_date.setUTCDate(day);
23655             test = function(){
23656                 return new_month != new_date.getUTCMonth();
23657             };
23658         }
23659         // Common date-resetting loop -- if date is beyond end of month, make it
23660         // end of month
23661         while (test()){
23662             new_date.setUTCDate(--day);
23663             new_date.setUTCMonth(new_month);
23664         }
23665         return new_date;
23666     },
23667
23668     moveYear: function(date, dir)
23669     {
23670         return this.moveMonth(date, dir*12);
23671     },
23672
23673     dateWithinRange: function(date)
23674     {
23675         return date >= this.startDate && date <= this.endDate;
23676     },
23677
23678     
23679     remove: function() 
23680     {
23681         this.picker().remove();
23682     },
23683     
23684     validateValue : function(value)
23685     {
23686         if(this.getVisibilityEl().hasClass('hidden')){
23687             return true;
23688         }
23689         
23690         if(value.length < 1)  {
23691             if(this.allowBlank){
23692                 return true;
23693             }
23694             return false;
23695         }
23696         
23697         if(value.length < this.minLength){
23698             return false;
23699         }
23700         if(value.length > this.maxLength){
23701             return false;
23702         }
23703         if(this.vtype){
23704             var vt = Roo.form.VTypes;
23705             if(!vt[this.vtype](value, this)){
23706                 return false;
23707             }
23708         }
23709         if(typeof this.validator == "function"){
23710             var msg = this.validator(value);
23711             if(msg !== true){
23712                 return false;
23713             }
23714         }
23715         
23716         if(this.regex && !this.regex.test(value)){
23717             return false;
23718         }
23719         
23720         if(typeof(this.parseDate(value)) == 'undefined'){
23721             return false;
23722         }
23723         
23724         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23725             return false;
23726         }      
23727         
23728         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23729             return false;
23730         } 
23731         
23732         
23733         return true;
23734     },
23735     
23736     reset : function()
23737     {
23738         this.date = this.viewDate = '';
23739         
23740         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23741     }
23742    
23743 });
23744
23745 Roo.apply(Roo.bootstrap.form.DateField,  {
23746     
23747     head : {
23748         tag: 'thead',
23749         cn: [
23750         {
23751             tag: 'tr',
23752             cn: [
23753             {
23754                 tag: 'th',
23755                 cls: 'prev',
23756                 html: '<i class="fa fa-arrow-left"/>'
23757             },
23758             {
23759                 tag: 'th',
23760                 cls: 'switch',
23761                 colspan: '5'
23762             },
23763             {
23764                 tag: 'th',
23765                 cls: 'next',
23766                 html: '<i class="fa fa-arrow-right"/>'
23767             }
23768
23769             ]
23770         }
23771         ]
23772     },
23773     
23774     content : {
23775         tag: 'tbody',
23776         cn: [
23777         {
23778             tag: 'tr',
23779             cn: [
23780             {
23781                 tag: 'td',
23782                 colspan: '7'
23783             }
23784             ]
23785         }
23786         ]
23787     },
23788     
23789     footer : {
23790         tag: 'tfoot',
23791         cn: [
23792         {
23793             tag: 'tr',
23794             cn: [
23795             {
23796                 tag: 'th',
23797                 colspan: '7',
23798                 cls: 'today'
23799             }
23800                     
23801             ]
23802         }
23803         ]
23804     },
23805     
23806     dates:{
23807         en: {
23808             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23809             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23810             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23811             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23812             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23813             today: "Today"
23814         }
23815     },
23816     
23817     modes: [
23818     {
23819         clsName: 'days',
23820         navFnc: 'Month',
23821         navStep: 1
23822     },
23823     {
23824         clsName: 'months',
23825         navFnc: 'FullYear',
23826         navStep: 1
23827     },
23828     {
23829         clsName: 'years',
23830         navFnc: 'FullYear',
23831         navStep: 10
23832     }]
23833 });
23834
23835 Roo.apply(Roo.bootstrap.form.DateField,  {
23836   
23837     template : {
23838         tag: 'div',
23839         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23840         cn: [
23841         {
23842             tag: 'div',
23843             cls: 'datepicker-days',
23844             cn: [
23845             {
23846                 tag: 'table',
23847                 cls: 'table-condensed',
23848                 cn:[
23849                 Roo.bootstrap.form.DateField.head,
23850                 {
23851                     tag: 'tbody'
23852                 },
23853                 Roo.bootstrap.form.DateField.footer
23854                 ]
23855             }
23856             ]
23857         },
23858         {
23859             tag: 'div',
23860             cls: 'datepicker-months',
23861             cn: [
23862             {
23863                 tag: 'table',
23864                 cls: 'table-condensed',
23865                 cn:[
23866                 Roo.bootstrap.form.DateField.head,
23867                 Roo.bootstrap.form.DateField.content,
23868                 Roo.bootstrap.form.DateField.footer
23869                 ]
23870             }
23871             ]
23872         },
23873         {
23874             tag: 'div',
23875             cls: 'datepicker-years',
23876             cn: [
23877             {
23878                 tag: 'table',
23879                 cls: 'table-condensed',
23880                 cn:[
23881                 Roo.bootstrap.form.DateField.head,
23882                 Roo.bootstrap.form.DateField.content,
23883                 Roo.bootstrap.form.DateField.footer
23884                 ]
23885             }
23886             ]
23887         }
23888         ]
23889     }
23890 });
23891
23892  
23893
23894  /*
23895  * - LGPL
23896  *
23897  * TimeField
23898  * 
23899  */
23900
23901 /**
23902  * @class Roo.bootstrap.form.TimeField
23903  * @extends Roo.bootstrap.form.Input
23904  * Bootstrap DateField class
23905  * 
23906  * 
23907  * @constructor
23908  * Create a new TimeField
23909  * @param {Object} config The config object
23910  */
23911
23912 Roo.bootstrap.form.TimeField = function(config){
23913     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23914     this.addEvents({
23915             /**
23916              * @event show
23917              * Fires when this field show.
23918              * @param {Roo.bootstrap.form.DateField} thisthis
23919              * @param {Mixed} date The date value
23920              */
23921             show : true,
23922             /**
23923              * @event show
23924              * Fires when this field hide.
23925              * @param {Roo.bootstrap.form.DateField} this
23926              * @param {Mixed} date The date value
23927              */
23928             hide : true,
23929             /**
23930              * @event select
23931              * Fires when select a date.
23932              * @param {Roo.bootstrap.form.DateField} this
23933              * @param {Mixed} date The date value
23934              */
23935             select : true
23936         });
23937 };
23938
23939 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23940     
23941     /**
23942      * @cfg {String} format
23943      * The default time format string which can be overriden for localization support.  The format must be
23944      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23945      */
23946     format : "H:i",
23947
23948     getAutoCreate : function()
23949     {
23950         this.after = '<i class="fa far fa-clock"></i>';
23951         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23952         
23953          
23954     },
23955     onRender: function(ct, position)
23956     {
23957         
23958         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23959                 
23960         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23961         
23962         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23963         
23964         this.pop = this.picker().select('>.datepicker-time',true).first();
23965         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23966         
23967         this.picker().on('mousedown', this.onMousedown, this);
23968         this.picker().on('click', this.onClick, this);
23969         
23970         this.picker().addClass('datepicker-dropdown');
23971     
23972         this.fillTime();
23973         this.update();
23974             
23975         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23976         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23977         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23978         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23979         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23980         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23981
23982     },
23983     
23984     fireKey: function(e){
23985         if (!this.picker().isVisible()){
23986             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23987                 this.show();
23988             }
23989             return;
23990         }
23991
23992         e.preventDefault();
23993         
23994         switch(e.keyCode){
23995             case 27: // escape
23996                 this.hide();
23997                 break;
23998             case 37: // left
23999             case 39: // right
24000                 this.onTogglePeriod();
24001                 break;
24002             case 38: // up
24003                 this.onIncrementMinutes();
24004                 break;
24005             case 40: // down
24006                 this.onDecrementMinutes();
24007                 break;
24008             case 13: // enter
24009             case 9: // tab
24010                 this.setTime();
24011                 break;
24012         }
24013     },
24014     
24015     onClick: function(e) {
24016         e.stopPropagation();
24017         e.preventDefault();
24018     },
24019     
24020     picker : function()
24021     {
24022         return this.pickerEl;
24023     },
24024     
24025     fillTime: function()
24026     {    
24027         var time = this.pop.select('tbody', true).first();
24028         
24029         time.dom.innerHTML = '';
24030         
24031         time.createChild({
24032             tag: 'tr',
24033             cn: [
24034                 {
24035                     tag: 'td',
24036                     cn: [
24037                         {
24038                             tag: 'a',
24039                             href: '#',
24040                             cls: 'btn',
24041                             cn: [
24042                                 {
24043                                     tag: 'i',
24044                                     cls: 'hours-up fa fas fa-chevron-up'
24045                                 }
24046                             ]
24047                         } 
24048                     ]
24049                 },
24050                 {
24051                     tag: 'td',
24052                     cls: 'separator'
24053                 },
24054                 {
24055                     tag: 'td',
24056                     cn: [
24057                         {
24058                             tag: 'a',
24059                             href: '#',
24060                             cls: 'btn',
24061                             cn: [
24062                                 {
24063                                     tag: 'i',
24064                                     cls: 'minutes-up fa fas fa-chevron-up'
24065                                 }
24066                             ]
24067                         }
24068                     ]
24069                 },
24070                 {
24071                     tag: 'td',
24072                     cls: 'separator'
24073                 }
24074             ]
24075         });
24076         
24077         time.createChild({
24078             tag: 'tr',
24079             cn: [
24080                 {
24081                     tag: 'td',
24082                     cn: [
24083                         {
24084                             tag: 'span',
24085                             cls: 'timepicker-hour',
24086                             html: '00'
24087                         }  
24088                     ]
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cls: 'separator',
24093                     html: ':'
24094                 },
24095                 {
24096                     tag: 'td',
24097                     cn: [
24098                         {
24099                             tag: 'span',
24100                             cls: 'timepicker-minute',
24101                             html: '00'
24102                         }  
24103                     ]
24104                 },
24105                 {
24106                     tag: 'td',
24107                     cls: 'separator'
24108                 },
24109                 {
24110                     tag: 'td',
24111                     cn: [
24112                         {
24113                             tag: 'button',
24114                             type: 'button',
24115                             cls: 'btn btn-primary period',
24116                             html: 'AM'
24117                             
24118                         }
24119                     ]
24120                 }
24121             ]
24122         });
24123         
24124         time.createChild({
24125             tag: 'tr',
24126             cn: [
24127                 {
24128                     tag: 'td',
24129                     cn: [
24130                         {
24131                             tag: 'a',
24132                             href: '#',
24133                             cls: 'btn',
24134                             cn: [
24135                                 {
24136                                     tag: 'span',
24137                                     cls: 'hours-down fa fas fa-chevron-down'
24138                                 }
24139                             ]
24140                         }
24141                     ]
24142                 },
24143                 {
24144                     tag: 'td',
24145                     cls: 'separator'
24146                 },
24147                 {
24148                     tag: 'td',
24149                     cn: [
24150                         {
24151                             tag: 'a',
24152                             href: '#',
24153                             cls: 'btn',
24154                             cn: [
24155                                 {
24156                                     tag: 'span',
24157                                     cls: 'minutes-down fa fas fa-chevron-down'
24158                                 }
24159                             ]
24160                         }
24161                     ]
24162                 },
24163                 {
24164                     tag: 'td',
24165                     cls: 'separator'
24166                 }
24167             ]
24168         });
24169         
24170     },
24171     
24172     update: function()
24173     {
24174         
24175         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24176         
24177         this.fill();
24178     },
24179     
24180     fill: function() 
24181     {
24182         var hours = this.time.getHours();
24183         var minutes = this.time.getMinutes();
24184         var period = 'AM';
24185         
24186         if(hours > 11){
24187             period = 'PM';
24188         }
24189         
24190         if(hours == 0){
24191             hours = 12;
24192         }
24193         
24194         
24195         if(hours > 12){
24196             hours = hours - 12;
24197         }
24198         
24199         if(hours < 10){
24200             hours = '0' + hours;
24201         }
24202         
24203         if(minutes < 10){
24204             minutes = '0' + minutes;
24205         }
24206         
24207         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24208         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24209         this.pop.select('button', true).first().dom.innerHTML = period;
24210         
24211     },
24212     
24213     place: function()
24214     {   
24215         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24216         
24217         var cls = ['bottom'];
24218         
24219         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24220             cls.pop();
24221             cls.push('top');
24222         }
24223         
24224         cls.push('right');
24225         
24226         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24227             cls.pop();
24228             cls.push('left');
24229         }
24230         //this.picker().setXY(20000,20000);
24231         this.picker().addClass(cls.join('-'));
24232         
24233         var _this = this;
24234         
24235         Roo.each(cls, function(c){
24236             if(c == 'bottom'){
24237                 (function() {
24238                  //  
24239                 }).defer(200);
24240                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24241                 //_this.picker().setTop(_this.inputEl().getHeight());
24242                 return;
24243             }
24244             if(c == 'top'){
24245                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24246                 
24247                 //_this.picker().setTop(0 - _this.picker().getHeight());
24248                 return;
24249             }
24250             /*
24251             if(c == 'left'){
24252                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24253                 return;
24254             }
24255             if(c == 'right'){
24256                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24257                 return;
24258             }
24259             */
24260         });
24261         
24262     },
24263   
24264     onFocus : function()
24265     {
24266         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24267         this.show();
24268     },
24269     
24270     onBlur : function()
24271     {
24272         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24273         this.hide();
24274     },
24275     
24276     show : function()
24277     {
24278         this.picker().show();
24279         this.pop.show();
24280         this.update();
24281         this.place();
24282         
24283         this.fireEvent('show', this, this.date);
24284     },
24285     
24286     hide : function()
24287     {
24288         this.picker().hide();
24289         this.pop.hide();
24290         
24291         this.fireEvent('hide', this, this.date);
24292     },
24293     
24294     setTime : function()
24295     {
24296         this.hide();
24297         this.setValue(this.time.format(this.format));
24298         
24299         this.fireEvent('select', this, this.date);
24300         
24301         
24302     },
24303     
24304     onMousedown: function(e){
24305         e.stopPropagation();
24306         e.preventDefault();
24307     },
24308     
24309     onIncrementHours: function()
24310     {
24311         Roo.log('onIncrementHours');
24312         this.time = this.time.add(Date.HOUR, 1);
24313         this.update();
24314         
24315     },
24316     
24317     onDecrementHours: function()
24318     {
24319         Roo.log('onDecrementHours');
24320         this.time = this.time.add(Date.HOUR, -1);
24321         this.update();
24322     },
24323     
24324     onIncrementMinutes: function()
24325     {
24326         Roo.log('onIncrementMinutes');
24327         this.time = this.time.add(Date.MINUTE, 1);
24328         this.update();
24329     },
24330     
24331     onDecrementMinutes: function()
24332     {
24333         Roo.log('onDecrementMinutes');
24334         this.time = this.time.add(Date.MINUTE, -1);
24335         this.update();
24336     },
24337     
24338     onTogglePeriod: function()
24339     {
24340         Roo.log('onTogglePeriod');
24341         this.time = this.time.add(Date.HOUR, 12);
24342         this.update();
24343     }
24344     
24345    
24346 });
24347  
24348
24349 Roo.apply(Roo.bootstrap.form.TimeField,  {
24350   
24351     template : {
24352         tag: 'div',
24353         cls: 'datepicker dropdown-menu',
24354         cn: [
24355             {
24356                 tag: 'div',
24357                 cls: 'datepicker-time',
24358                 cn: [
24359                 {
24360                     tag: 'table',
24361                     cls: 'table-condensed',
24362                     cn:[
24363                         {
24364                             tag: 'tbody',
24365                             cn: [
24366                                 {
24367                                     tag: 'tr',
24368                                     cn: [
24369                                     {
24370                                         tag: 'td',
24371                                         colspan: '7'
24372                                     }
24373                                     ]
24374                                 }
24375                             ]
24376                         },
24377                         {
24378                             tag: 'tfoot',
24379                             cn: [
24380                                 {
24381                                     tag: 'tr',
24382                                     cn: [
24383                                     {
24384                                         tag: 'th',
24385                                         colspan: '7',
24386                                         cls: '',
24387                                         cn: [
24388                                             {
24389                                                 tag: 'button',
24390                                                 cls: 'btn btn-info ok',
24391                                                 html: 'OK'
24392                                             }
24393                                         ]
24394                                     }
24395                     
24396                                     ]
24397                                 }
24398                             ]
24399                         }
24400                     ]
24401                 }
24402                 ]
24403             }
24404         ]
24405     }
24406 });
24407
24408  
24409
24410  /*
24411  * - LGPL
24412  *
24413  * MonthField
24414  * 
24415  */
24416
24417 /**
24418  * @class Roo.bootstrap.form.MonthField
24419  * @extends Roo.bootstrap.form.Input
24420  * Bootstrap MonthField class
24421  * 
24422  * @cfg {String} language default en
24423  * 
24424  * @constructor
24425  * Create a new MonthField
24426  * @param {Object} config The config object
24427  */
24428
24429 Roo.bootstrap.form.MonthField = function(config){
24430     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24431     
24432     this.addEvents({
24433         /**
24434          * @event show
24435          * Fires when this field show.
24436          * @param {Roo.bootstrap.form.MonthField} this
24437          * @param {Mixed} date The date value
24438          */
24439         show : true,
24440         /**
24441          * @event show
24442          * Fires when this field hide.
24443          * @param {Roo.bootstrap.form.MonthField} this
24444          * @param {Mixed} date The date value
24445          */
24446         hide : true,
24447         /**
24448          * @event select
24449          * Fires when select a date.
24450          * @param {Roo.bootstrap.form.MonthField} this
24451          * @param {String} oldvalue The old value
24452          * @param {String} newvalue The new value
24453          */
24454         select : true
24455     });
24456 };
24457
24458 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24459     
24460     onRender: function(ct, position)
24461     {
24462         
24463         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24464         
24465         this.language = this.language || 'en';
24466         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24467         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24468         
24469         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24470         this.isInline = false;
24471         this.isInput = true;
24472         this.component = this.el.select('.add-on', true).first() || false;
24473         this.component = (this.component && this.component.length === 0) ? false : this.component;
24474         this.hasInput = this.component && this.inputEL().length;
24475         
24476         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24477         
24478         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24479         
24480         this.picker().on('mousedown', this.onMousedown, this);
24481         this.picker().on('click', this.onClick, this);
24482         
24483         this.picker().addClass('datepicker-dropdown');
24484         
24485         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24486             v.setStyle('width', '189px');
24487         });
24488         
24489         this.fillMonths();
24490         
24491         this.update();
24492         
24493         if(this.isInline) {
24494             this.show();
24495         }
24496         
24497     },
24498     
24499     setValue: function(v, suppressEvent)
24500     {   
24501         var o = this.getValue();
24502         
24503         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24504         
24505         this.update();
24506
24507         if(suppressEvent !== true){
24508             this.fireEvent('select', this, o, v);
24509         }
24510         
24511     },
24512     
24513     getValue: function()
24514     {
24515         return this.value;
24516     },
24517     
24518     onClick: function(e) 
24519     {
24520         e.stopPropagation();
24521         e.preventDefault();
24522         
24523         var target = e.getTarget();
24524         
24525         if(target.nodeName.toLowerCase() === 'i'){
24526             target = Roo.get(target).dom.parentNode;
24527         }
24528         
24529         var nodeName = target.nodeName;
24530         var className = target.className;
24531         var html = target.innerHTML;
24532         
24533         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24534             return;
24535         }
24536         
24537         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24538         
24539         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24540         
24541         this.hide();
24542                         
24543     },
24544     
24545     picker : function()
24546     {
24547         return this.pickerEl;
24548     },
24549     
24550     fillMonths: function()
24551     {    
24552         var i = 0;
24553         var months = this.picker().select('>.datepicker-months td', true).first();
24554         
24555         months.dom.innerHTML = '';
24556         
24557         while (i < 12) {
24558             var month = {
24559                 tag: 'span',
24560                 cls: 'month',
24561                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24562             };
24563             
24564             months.createChild(month);
24565         }
24566         
24567     },
24568     
24569     update: function()
24570     {
24571         var _this = this;
24572         
24573         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24574             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24575         }
24576         
24577         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24578             e.removeClass('active');
24579             
24580             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24581                 e.addClass('active');
24582             }
24583         })
24584     },
24585     
24586     place: function()
24587     {
24588         if(this.isInline) {
24589             return;
24590         }
24591         
24592         this.picker().removeClass(['bottom', 'top']);
24593         
24594         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24595             /*
24596              * place to the top of element!
24597              *
24598              */
24599             
24600             this.picker().addClass('top');
24601             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24602             
24603             return;
24604         }
24605         
24606         this.picker().addClass('bottom');
24607         
24608         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24609     },
24610     
24611     onFocus : function()
24612     {
24613         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24614         this.show();
24615     },
24616     
24617     onBlur : function()
24618     {
24619         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24620         
24621         var d = this.inputEl().getValue();
24622         
24623         this.setValue(d);
24624                 
24625         this.hide();
24626     },
24627     
24628     show : function()
24629     {
24630         this.picker().show();
24631         this.picker().select('>.datepicker-months', true).first().show();
24632         this.update();
24633         this.place();
24634         
24635         this.fireEvent('show', this, this.date);
24636     },
24637     
24638     hide : function()
24639     {
24640         if(this.isInline) {
24641             return;
24642         }
24643         this.picker().hide();
24644         this.fireEvent('hide', this, this.date);
24645         
24646     },
24647     
24648     onMousedown: function(e)
24649     {
24650         e.stopPropagation();
24651         e.preventDefault();
24652     },
24653     
24654     keyup: function(e)
24655     {
24656         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24657         this.update();
24658     },
24659
24660     fireKey: function(e)
24661     {
24662         if (!this.picker().isVisible()){
24663             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24664                 this.show();
24665             }
24666             return;
24667         }
24668         
24669         var dir;
24670         
24671         switch(e.keyCode){
24672             case 27: // escape
24673                 this.hide();
24674                 e.preventDefault();
24675                 break;
24676             case 37: // left
24677             case 39: // right
24678                 dir = e.keyCode == 37 ? -1 : 1;
24679                 
24680                 this.vIndex = this.vIndex + dir;
24681                 
24682                 if(this.vIndex < 0){
24683                     this.vIndex = 0;
24684                 }
24685                 
24686                 if(this.vIndex > 11){
24687                     this.vIndex = 11;
24688                 }
24689                 
24690                 if(isNaN(this.vIndex)){
24691                     this.vIndex = 0;
24692                 }
24693                 
24694                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24695                 
24696                 break;
24697             case 38: // up
24698             case 40: // down
24699                 
24700                 dir = e.keyCode == 38 ? -1 : 1;
24701                 
24702                 this.vIndex = this.vIndex + dir * 4;
24703                 
24704                 if(this.vIndex < 0){
24705                     this.vIndex = 0;
24706                 }
24707                 
24708                 if(this.vIndex > 11){
24709                     this.vIndex = 11;
24710                 }
24711                 
24712                 if(isNaN(this.vIndex)){
24713                     this.vIndex = 0;
24714                 }
24715                 
24716                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24717                 break;
24718                 
24719             case 13: // enter
24720                 
24721                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24722                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24723                 }
24724                 
24725                 this.hide();
24726                 e.preventDefault();
24727                 break;
24728             case 9: // tab
24729                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24730                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24731                 }
24732                 this.hide();
24733                 break;
24734             case 16: // shift
24735             case 17: // ctrl
24736             case 18: // alt
24737                 break;
24738             default :
24739                 this.hide();
24740                 
24741         }
24742     },
24743     
24744     remove: function() 
24745     {
24746         this.picker().remove();
24747     }
24748    
24749 });
24750
24751 Roo.apply(Roo.bootstrap.form.MonthField,  {
24752     
24753     content : {
24754         tag: 'tbody',
24755         cn: [
24756         {
24757             tag: 'tr',
24758             cn: [
24759             {
24760                 tag: 'td',
24761                 colspan: '7'
24762             }
24763             ]
24764         }
24765         ]
24766     },
24767     
24768     dates:{
24769         en: {
24770             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24771             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24772         }
24773     }
24774 });
24775
24776 Roo.apply(Roo.bootstrap.form.MonthField,  {
24777   
24778     template : {
24779         tag: 'div',
24780         cls: 'datepicker dropdown-menu roo-dynamic',
24781         cn: [
24782             {
24783                 tag: 'div',
24784                 cls: 'datepicker-months',
24785                 cn: [
24786                 {
24787                     tag: 'table',
24788                     cls: 'table-condensed',
24789                     cn:[
24790                         Roo.bootstrap.form.DateField.content
24791                     ]
24792                 }
24793                 ]
24794             }
24795         ]
24796     }
24797 });
24798
24799  
24800
24801  
24802  /*
24803  * - LGPL
24804  *
24805  * CheckBox
24806  * 
24807  */
24808
24809 /**
24810  * @class Roo.bootstrap.form.CheckBox
24811  * @extends Roo.bootstrap.form.Input
24812  * Bootstrap CheckBox class
24813  * 
24814  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24815  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24816  * @cfg {String} boxLabel The text that appears beside the checkbox
24817  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24818  * @cfg {Boolean} checked initnal the element
24819  * @cfg {Boolean} inline inline the element (default false)
24820  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24821  * @cfg {String} tooltip label tooltip
24822  * 
24823  * @constructor
24824  * Create a new CheckBox
24825  * @param {Object} config The config object
24826  */
24827
24828 Roo.bootstrap.form.CheckBox = function(config){
24829     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24830    
24831     this.addEvents({
24832         /**
24833         * @event check
24834         * Fires when the element is checked or unchecked.
24835         * @param {Roo.bootstrap.form.CheckBox} this This input
24836         * @param {Boolean} checked The new checked value
24837         */
24838        check : true,
24839        /**
24840         * @event click
24841         * Fires when the element is click.
24842         * @param {Roo.bootstrap.form.CheckBox} this This input
24843         */
24844        click : true
24845     });
24846     
24847 };
24848
24849 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24850   
24851     inputType: 'checkbox',
24852     inputValue: 1,
24853     valueOff: 0,
24854     boxLabel: false,
24855     checked: false,
24856     weight : false,
24857     inline: false,
24858     tooltip : '',
24859     
24860     // checkbox success does not make any sense really.. 
24861     invalidClass : "",
24862     validClass : "",
24863     
24864     
24865     getAutoCreate : function()
24866     {
24867         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24868         
24869         var id = Roo.id();
24870         
24871         var cfg = {};
24872         
24873         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24874         
24875         if(this.inline){
24876             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24877         }
24878         
24879         var input =  {
24880             tag: 'input',
24881             id : id,
24882             type : this.inputType,
24883             value : this.inputValue,
24884             cls : 'roo-' + this.inputType, //'form-box',
24885             placeholder : this.placeholder || ''
24886             
24887         };
24888         
24889         if(this.inputType != 'radio'){
24890             var hidden =  {
24891                 tag: 'input',
24892                 type : 'hidden',
24893                 cls : 'roo-hidden-value',
24894                 value : this.checked ? this.inputValue : this.valueOff
24895             };
24896         }
24897         
24898             
24899         if (this.weight) { // Validity check?
24900             cfg.cls += " " + this.inputType + "-" + this.weight;
24901         }
24902         
24903         if (this.disabled) {
24904             input.disabled=true;
24905         }
24906         
24907         if(this.checked){
24908             input.checked = this.checked;
24909         }
24910         
24911         if (this.name) {
24912             
24913             input.name = this.name;
24914             
24915             if(this.inputType != 'radio'){
24916                 hidden.name = this.name;
24917                 input.name = '_hidden_' + this.name;
24918             }
24919         }
24920         
24921         if (this.size) {
24922             input.cls += ' input-' + this.size;
24923         }
24924         
24925         var settings=this;
24926         
24927         ['xs','sm','md','lg'].map(function(size){
24928             if (settings[size]) {
24929                 cfg.cls += ' col-' + size + '-' + settings[size];
24930             }
24931         });
24932         
24933         var inputblock = input;
24934          
24935         if (this.before || this.after) {
24936             
24937             inputblock = {
24938                 cls : 'input-group',
24939                 cn :  [] 
24940             };
24941             
24942             if (this.before) {
24943                 inputblock.cn.push({
24944                     tag :'span',
24945                     cls : 'input-group-addon',
24946                     html : this.before
24947                 });
24948             }
24949             
24950             inputblock.cn.push(input);
24951             
24952             if(this.inputType != 'radio'){
24953                 inputblock.cn.push(hidden);
24954             }
24955             
24956             if (this.after) {
24957                 inputblock.cn.push({
24958                     tag :'span',
24959                     cls : 'input-group-addon',
24960                     html : this.after
24961                 });
24962             }
24963             
24964         }
24965         var boxLabelCfg = false;
24966         
24967         if(this.boxLabel){
24968            
24969             boxLabelCfg = {
24970                 tag: 'label',
24971                 //'for': id, // box label is handled by onclick - so no for...
24972                 cls: 'box-label',
24973                 html: this.boxLabel
24974             };
24975             if(this.tooltip){
24976                 boxLabelCfg.tooltip = this.tooltip;
24977             }
24978              
24979         }
24980         
24981         
24982         if (align ==='left' && this.fieldLabel.length) {
24983 //                Roo.log("left and has label");
24984             cfg.cn = [
24985                 {
24986                     tag: 'label',
24987                     'for' :  id,
24988                     cls : 'control-label',
24989                     html : this.fieldLabel
24990                 },
24991                 {
24992                     cls : "", 
24993                     cn: [
24994                         inputblock
24995                     ]
24996                 }
24997             ];
24998             
24999             if (boxLabelCfg) {
25000                 cfg.cn[1].cn.push(boxLabelCfg);
25001             }
25002             
25003             if(this.labelWidth > 12){
25004                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25005             }
25006             
25007             if(this.labelWidth < 13 && this.labelmd == 0){
25008                 this.labelmd = this.labelWidth;
25009             }
25010             
25011             if(this.labellg > 0){
25012                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25013                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25014             }
25015             
25016             if(this.labelmd > 0){
25017                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25018                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25019             }
25020             
25021             if(this.labelsm > 0){
25022                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25023                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25024             }
25025             
25026             if(this.labelxs > 0){
25027                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25028                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25029             }
25030             
25031         } else if ( this.fieldLabel.length) {
25032 //                Roo.log(" label");
25033                 cfg.cn = [
25034                    
25035                     {
25036                         tag: this.boxLabel ? 'span' : 'label',
25037                         'for': id,
25038                         cls: 'control-label box-input-label',
25039                         //cls : 'input-group-addon',
25040                         html : this.fieldLabel
25041                     },
25042                     
25043                     inputblock
25044                     
25045                 ];
25046                 if (boxLabelCfg) {
25047                     cfg.cn.push(boxLabelCfg);
25048                 }
25049
25050         } else {
25051             
25052 //                Roo.log(" no label && no align");
25053                 cfg.cn = [  inputblock ] ;
25054                 if (boxLabelCfg) {
25055                     cfg.cn.push(boxLabelCfg);
25056                 }
25057
25058                 
25059         }
25060         
25061        
25062         
25063         if(this.inputType != 'radio'){
25064             cfg.cn.push(hidden);
25065         }
25066         
25067         return cfg;
25068         
25069     },
25070     
25071     /**
25072      * return the real input element.
25073      */
25074     inputEl: function ()
25075     {
25076         return this.el.select('input.roo-' + this.inputType,true).first();
25077     },
25078     hiddenEl: function ()
25079     {
25080         return this.el.select('input.roo-hidden-value',true).first();
25081     },
25082     
25083     labelEl: function()
25084     {
25085         return this.el.select('label.control-label',true).first();
25086     },
25087     /* depricated... */
25088     
25089     label: function()
25090     {
25091         return this.labelEl();
25092     },
25093     
25094     boxLabelEl: function()
25095     {
25096         return this.el.select('label.box-label',true).first();
25097     },
25098     
25099     initEvents : function()
25100     {
25101 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25102         
25103         this.inputEl().on('click', this.onClick,  this);
25104         
25105         if (this.boxLabel) { 
25106             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25107         }
25108         
25109         this.startValue = this.getValue();
25110         
25111         if(this.groupId){
25112             Roo.bootstrap.form.CheckBox.register(this);
25113         }
25114     },
25115     
25116     onClick : function(e)
25117     {   
25118         if(this.fireEvent('click', this, e) !== false){
25119             this.setChecked(!this.checked);
25120         }
25121         
25122     },
25123     
25124     setChecked : function(state,suppressEvent)
25125     {
25126         this.startValue = this.getValue();
25127
25128         if(this.inputType == 'radio'){
25129             
25130             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25131                 e.dom.checked = false;
25132             });
25133             
25134             this.inputEl().dom.checked = true;
25135             
25136             this.inputEl().dom.value = this.inputValue;
25137             
25138             if(suppressEvent !== true){
25139                 this.fireEvent('check', this, true);
25140             }
25141             
25142             this.validate();
25143             
25144             return;
25145         }
25146         
25147         this.checked = state;
25148         
25149         this.inputEl().dom.checked = state;
25150         
25151         
25152         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25153         
25154         if(suppressEvent !== true){
25155             this.fireEvent('check', this, state);
25156         }
25157         
25158         this.validate();
25159     },
25160     
25161     getValue : function()
25162     {
25163         if(this.inputType == 'radio'){
25164             return this.getGroupValue();
25165         }
25166         
25167         return this.hiddenEl().dom.value;
25168         
25169     },
25170     
25171     getGroupValue : function()
25172     {
25173         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25174             return '';
25175         }
25176         
25177         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25178     },
25179     
25180     setValue : function(v,suppressEvent)
25181     {
25182         if(this.inputType == 'radio'){
25183             this.setGroupValue(v, suppressEvent);
25184             return;
25185         }
25186         
25187         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25188         
25189         this.validate();
25190     },
25191     
25192     setGroupValue : function(v, suppressEvent)
25193     {
25194         this.startValue = this.getValue();
25195         
25196         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25197             e.dom.checked = false;
25198             
25199             if(e.dom.value == v){
25200                 e.dom.checked = true;
25201             }
25202         });
25203         
25204         if(suppressEvent !== true){
25205             this.fireEvent('check', this, true);
25206         }
25207
25208         this.validate();
25209         
25210         return;
25211     },
25212     
25213     validate : function()
25214     {
25215         if(this.getVisibilityEl().hasClass('hidden')){
25216             return true;
25217         }
25218         
25219         if(
25220                 this.disabled || 
25221                 (this.inputType == 'radio' && this.validateRadio()) ||
25222                 (this.inputType == 'checkbox' && this.validateCheckbox())
25223         ){
25224             this.markValid();
25225             return true;
25226         }
25227         
25228         this.markInvalid();
25229         return false;
25230     },
25231     
25232     validateRadio : function()
25233     {
25234         if(this.getVisibilityEl().hasClass('hidden')){
25235             return true;
25236         }
25237         
25238         if(this.allowBlank){
25239             return true;
25240         }
25241         
25242         var valid = false;
25243         
25244         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25245             if(!e.dom.checked){
25246                 return;
25247             }
25248             
25249             valid = true;
25250             
25251             return false;
25252         });
25253         
25254         return valid;
25255     },
25256     
25257     validateCheckbox : function()
25258     {
25259         if(!this.groupId){
25260             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25261             //return (this.getValue() == this.inputValue) ? true : false;
25262         }
25263         
25264         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25265         
25266         if(!group){
25267             return false;
25268         }
25269         
25270         var r = false;
25271         
25272         for(var i in group){
25273             if(group[i].el.isVisible(true)){
25274                 r = false;
25275                 break;
25276             }
25277             
25278             r = true;
25279         }
25280         
25281         for(var i in group){
25282             if(r){
25283                 break;
25284             }
25285             
25286             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25287         }
25288         
25289         return r;
25290     },
25291     
25292     /**
25293      * Mark this field as valid
25294      */
25295     markValid : function()
25296     {
25297         var _this = this;
25298         
25299         this.fireEvent('valid', this);
25300         
25301         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25302         
25303         if(this.groupId){
25304             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25305         }
25306         
25307         if(label){
25308             label.markValid();
25309         }
25310
25311         if(this.inputType == 'radio'){
25312             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25313                 var fg = e.findParent('.form-group', false, true);
25314                 if (Roo.bootstrap.version == 3) {
25315                     fg.removeClass([_this.invalidClass, _this.validClass]);
25316                     fg.addClass(_this.validClass);
25317                 } else {
25318                     fg.removeClass(['is-valid', 'is-invalid']);
25319                     fg.addClass('is-valid');
25320                 }
25321             });
25322             
25323             return;
25324         }
25325
25326         if(!this.groupId){
25327             var fg = this.el.findParent('.form-group', false, true);
25328             if (Roo.bootstrap.version == 3) {
25329                 fg.removeClass([this.invalidClass, this.validClass]);
25330                 fg.addClass(this.validClass);
25331             } else {
25332                 fg.removeClass(['is-valid', 'is-invalid']);
25333                 fg.addClass('is-valid');
25334             }
25335             return;
25336         }
25337         
25338         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25339         
25340         if(!group){
25341             return;
25342         }
25343         
25344         for(var i in group){
25345             var fg = group[i].el.findParent('.form-group', false, true);
25346             if (Roo.bootstrap.version == 3) {
25347                 fg.removeClass([this.invalidClass, this.validClass]);
25348                 fg.addClass(this.validClass);
25349             } else {
25350                 fg.removeClass(['is-valid', 'is-invalid']);
25351                 fg.addClass('is-valid');
25352             }
25353         }
25354     },
25355     
25356      /**
25357      * Mark this field as invalid
25358      * @param {String} msg The validation message
25359      */
25360     markInvalid : function(msg)
25361     {
25362         if(this.allowBlank){
25363             return;
25364         }
25365         
25366         var _this = this;
25367         
25368         this.fireEvent('invalid', this, msg);
25369         
25370         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25371         
25372         if(this.groupId){
25373             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25374         }
25375         
25376         if(label){
25377             label.markInvalid();
25378         }
25379             
25380         if(this.inputType == 'radio'){
25381             
25382             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25383                 var fg = e.findParent('.form-group', false, true);
25384                 if (Roo.bootstrap.version == 3) {
25385                     fg.removeClass([_this.invalidClass, _this.validClass]);
25386                     fg.addClass(_this.invalidClass);
25387                 } else {
25388                     fg.removeClass(['is-invalid', 'is-valid']);
25389                     fg.addClass('is-invalid');
25390                 }
25391             });
25392             
25393             return;
25394         }
25395         
25396         if(!this.groupId){
25397             var fg = this.el.findParent('.form-group', false, true);
25398             if (Roo.bootstrap.version == 3) {
25399                 fg.removeClass([_this.invalidClass, _this.validClass]);
25400                 fg.addClass(_this.invalidClass);
25401             } else {
25402                 fg.removeClass(['is-invalid', 'is-valid']);
25403                 fg.addClass('is-invalid');
25404             }
25405             return;
25406         }
25407         
25408         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25409         
25410         if(!group){
25411             return;
25412         }
25413         
25414         for(var i in group){
25415             var fg = group[i].el.findParent('.form-group', false, true);
25416             if (Roo.bootstrap.version == 3) {
25417                 fg.removeClass([_this.invalidClass, _this.validClass]);
25418                 fg.addClass(_this.invalidClass);
25419             } else {
25420                 fg.removeClass(['is-invalid', 'is-valid']);
25421                 fg.addClass('is-invalid');
25422             }
25423         }
25424         
25425     },
25426     
25427     clearInvalid : function()
25428     {
25429         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25430         
25431         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25432         
25433         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25434         
25435         if (label && label.iconEl) {
25436             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25437             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25438         }
25439     },
25440     
25441     disable : function()
25442     {
25443         if(this.inputType != 'radio'){
25444             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25445             return;
25446         }
25447         
25448         var _this = this;
25449         
25450         if(this.rendered){
25451             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25452                 _this.getActionEl().addClass(this.disabledClass);
25453                 e.dom.disabled = true;
25454             });
25455         }
25456         
25457         this.disabled = true;
25458         this.fireEvent("disable", this);
25459         return this;
25460     },
25461
25462     enable : function()
25463     {
25464         if(this.inputType != 'radio'){
25465             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25466             return;
25467         }
25468         
25469         var _this = this;
25470         
25471         if(this.rendered){
25472             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25473                 _this.getActionEl().removeClass(this.disabledClass);
25474                 e.dom.disabled = false;
25475             });
25476         }
25477         
25478         this.disabled = false;
25479         this.fireEvent("enable", this);
25480         return this;
25481     },
25482     
25483     setBoxLabel : function(v)
25484     {
25485         this.boxLabel = v;
25486         
25487         if(this.rendered){
25488             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25489         }
25490     }
25491
25492 });
25493
25494 Roo.apply(Roo.bootstrap.form.CheckBox, {
25495     
25496     groups: {},
25497     
25498      /**
25499     * register a CheckBox Group
25500     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25501     */
25502     register : function(checkbox)
25503     {
25504         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25505             this.groups[checkbox.groupId] = {};
25506         }
25507         
25508         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25509             return;
25510         }
25511         
25512         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25513         
25514     },
25515     /**
25516     * fetch a CheckBox Group based on the group ID
25517     * @param {string} the group ID
25518     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25519     */
25520     get: function(groupId) {
25521         if (typeof(this.groups[groupId]) == 'undefined') {
25522             return false;
25523         }
25524         
25525         return this.groups[groupId] ;
25526     }
25527     
25528     
25529 });
25530 /*
25531  * - LGPL
25532  *
25533  * RadioItem
25534  * 
25535  */
25536
25537 /**
25538  * @class Roo.bootstrap.form.Radio
25539  * @extends Roo.bootstrap.Component
25540  * Bootstrap Radio class
25541  * @cfg {String} boxLabel - the label associated
25542  * @cfg {String} value - the value of radio
25543  * 
25544  * @constructor
25545  * Create a new Radio
25546  * @param {Object} config The config object
25547  */
25548 Roo.bootstrap.form.Radio = function(config){
25549     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25550     
25551 };
25552
25553 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25554     
25555     boxLabel : '',
25556     
25557     value : '',
25558     
25559     getAutoCreate : function()
25560     {
25561         var cfg = {
25562             tag : 'div',
25563             cls : 'form-group radio',
25564             cn : [
25565                 {
25566                     tag : 'label',
25567                     cls : 'box-label',
25568                     html : this.boxLabel
25569                 }
25570             ]
25571         };
25572         
25573         return cfg;
25574     },
25575     
25576     initEvents : function() 
25577     {
25578         this.parent().register(this);
25579         
25580         this.el.on('click', this.onClick, this);
25581         
25582     },
25583     
25584     onClick : function(e)
25585     {
25586         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25587             this.setChecked(true);
25588         }
25589     },
25590     
25591     setChecked : function(state, suppressEvent)
25592     {
25593         this.parent().setValue(this.value, suppressEvent);
25594         
25595     },
25596     
25597     setBoxLabel : function(v)
25598     {
25599         this.boxLabel = v;
25600         
25601         if(this.rendered){
25602             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25603         }
25604     }
25605     
25606 });
25607  
25608
25609  /*
25610  * - LGPL
25611  *
25612  * Input
25613  * 
25614  */
25615
25616 /**
25617  * @class Roo.bootstrap.form.SecurePass
25618  * @extends Roo.bootstrap.form.Input
25619  * Bootstrap SecurePass class
25620  *
25621  * 
25622  * @constructor
25623  * Create a new SecurePass
25624  * @param {Object} config The config object
25625  */
25626  
25627 Roo.bootstrap.form.SecurePass = function (config) {
25628     // these go here, so the translation tool can replace them..
25629     this.errors = {
25630         PwdEmpty: "Please type a password, and then retype it to confirm.",
25631         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25632         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25633         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25634         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25635         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25636         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25637         TooWeak: "Your password is Too Weak."
25638     },
25639     this.meterLabel = "Password strength:";
25640     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25641     this.meterClass = [
25642         "roo-password-meter-tooweak", 
25643         "roo-password-meter-weak", 
25644         "roo-password-meter-medium", 
25645         "roo-password-meter-strong", 
25646         "roo-password-meter-grey"
25647     ];
25648     
25649     this.errors = {};
25650     
25651     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25652 }
25653
25654 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25655     /**
25656      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25657      * {
25658      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25659      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25660      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25661      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25662      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25663      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25664      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25665      * })
25666      */
25667     // private
25668     
25669     meterWidth: 300,
25670     errorMsg :'',    
25671     errors: false,
25672     imageRoot: '/',
25673     /**
25674      * @cfg {String/Object} Label for the strength meter (defaults to
25675      * 'Password strength:')
25676      */
25677     // private
25678     meterLabel: '',
25679     /**
25680      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25681      * ['Weak', 'Medium', 'Strong'])
25682      */
25683     // private    
25684     pwdStrengths: false,    
25685     // private
25686     strength: 0,
25687     // private
25688     _lastPwd: null,
25689     // private
25690     kCapitalLetter: 0,
25691     kSmallLetter: 1,
25692     kDigit: 2,
25693     kPunctuation: 3,
25694     
25695     insecure: false,
25696     // private
25697     initEvents: function ()
25698     {
25699         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25700
25701         if (this.el.is('input[type=password]') && Roo.isSafari) {
25702             this.el.on('keydown', this.SafariOnKeyDown, this);
25703         }
25704
25705         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25706     },
25707     // private
25708     onRender: function (ct, position)
25709     {
25710         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25711         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25712         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25713
25714         this.trigger.createChild({
25715                    cn: [
25716                     {
25717                     //id: 'PwdMeter',
25718                     tag: 'div',
25719                     cls: 'roo-password-meter-grey col-xs-12',
25720                     style: {
25721                         //width: 0,
25722                         //width: this.meterWidth + 'px'                                                
25723                         }
25724                     },
25725                     {                            
25726                          cls: 'roo-password-meter-text'                          
25727                     }
25728                 ]            
25729         });
25730
25731          
25732         if (this.hideTrigger) {
25733             this.trigger.setDisplayed(false);
25734         }
25735         this.setSize(this.width || '', this.height || '');
25736     },
25737     // private
25738     onDestroy: function ()
25739     {
25740         if (this.trigger) {
25741             this.trigger.removeAllListeners();
25742             this.trigger.remove();
25743         }
25744         if (this.wrap) {
25745             this.wrap.remove();
25746         }
25747         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25748     },
25749     // private
25750     checkStrength: function ()
25751     {
25752         var pwd = this.inputEl().getValue();
25753         if (pwd == this._lastPwd) {
25754             return;
25755         }
25756
25757         var strength;
25758         if (this.ClientSideStrongPassword(pwd)) {
25759             strength = 3;
25760         } else if (this.ClientSideMediumPassword(pwd)) {
25761             strength = 2;
25762         } else if (this.ClientSideWeakPassword(pwd)) {
25763             strength = 1;
25764         } else {
25765             strength = 0;
25766         }
25767         
25768         Roo.log('strength1: ' + strength);
25769         
25770         //var pm = this.trigger.child('div/div/div').dom;
25771         var pm = this.trigger.child('div/div');
25772         pm.removeClass(this.meterClass);
25773         pm.addClass(this.meterClass[strength]);
25774                 
25775         
25776         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25777                 
25778         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25779         
25780         this._lastPwd = pwd;
25781     },
25782     reset: function ()
25783     {
25784         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25785         
25786         this._lastPwd = '';
25787         
25788         var pm = this.trigger.child('div/div');
25789         pm.removeClass(this.meterClass);
25790         pm.addClass('roo-password-meter-grey');        
25791         
25792         
25793         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25794         
25795         pt.innerHTML = '';
25796         this.inputEl().dom.type='password';
25797     },
25798     // private
25799     validateValue: function (value)
25800     {
25801         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25802             return false;
25803         }
25804         if (value.length == 0) {
25805             if (this.allowBlank) {
25806                 this.clearInvalid();
25807                 return true;
25808             }
25809
25810             this.markInvalid(this.errors.PwdEmpty);
25811             this.errorMsg = this.errors.PwdEmpty;
25812             return false;
25813         }
25814         
25815         if(this.insecure){
25816             return true;
25817         }
25818         
25819         if (!value.match(/[\x21-\x7e]+/)) {
25820             this.markInvalid(this.errors.PwdBadChar);
25821             this.errorMsg = this.errors.PwdBadChar;
25822             return false;
25823         }
25824         if (value.length < 6) {
25825             this.markInvalid(this.errors.PwdShort);
25826             this.errorMsg = this.errors.PwdShort;
25827             return false;
25828         }
25829         if (value.length > 16) {
25830             this.markInvalid(this.errors.PwdLong);
25831             this.errorMsg = this.errors.PwdLong;
25832             return false;
25833         }
25834         var strength;
25835         if (this.ClientSideStrongPassword(value)) {
25836             strength = 3;
25837         } else if (this.ClientSideMediumPassword(value)) {
25838             strength = 2;
25839         } else if (this.ClientSideWeakPassword(value)) {
25840             strength = 1;
25841         } else {
25842             strength = 0;
25843         }
25844
25845         
25846         if (strength < 2) {
25847             //this.markInvalid(this.errors.TooWeak);
25848             this.errorMsg = this.errors.TooWeak;
25849             //return false;
25850         }
25851         
25852         
25853         console.log('strength2: ' + strength);
25854         
25855         //var pm = this.trigger.child('div/div/div').dom;
25856         
25857         var pm = this.trigger.child('div/div');
25858         pm.removeClass(this.meterClass);
25859         pm.addClass(this.meterClass[strength]);
25860                 
25861         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25862                 
25863         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25864         
25865         this.errorMsg = ''; 
25866         return true;
25867     },
25868     // private
25869     CharacterSetChecks: function (type)
25870     {
25871         this.type = type;
25872         this.fResult = false;
25873     },
25874     // private
25875     isctype: function (character, type)
25876     {
25877         switch (type) {  
25878             case this.kCapitalLetter:
25879                 if (character >= 'A' && character <= 'Z') {
25880                     return true;
25881                 }
25882                 break;
25883             
25884             case this.kSmallLetter:
25885                 if (character >= 'a' && character <= 'z') {
25886                     return true;
25887                 }
25888                 break;
25889             
25890             case this.kDigit:
25891                 if (character >= '0' && character <= '9') {
25892                     return true;
25893                 }
25894                 break;
25895             
25896             case this.kPunctuation:
25897                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25898                     return true;
25899                 }
25900                 break;
25901             
25902             default:
25903                 return false;
25904         }
25905
25906     },
25907     // private
25908     IsLongEnough: function (pwd, size)
25909     {
25910         return !(pwd == null || isNaN(size) || pwd.length < size);
25911     },
25912     // private
25913     SpansEnoughCharacterSets: function (word, nb)
25914     {
25915         if (!this.IsLongEnough(word, nb))
25916         {
25917             return false;
25918         }
25919
25920         var characterSetChecks = new Array(
25921             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25922             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25923         );
25924         
25925         for (var index = 0; index < word.length; ++index) {
25926             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25927                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25928                     characterSetChecks[nCharSet].fResult = true;
25929                     break;
25930                 }
25931             }
25932         }
25933
25934         var nCharSets = 0;
25935         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25936             if (characterSetChecks[nCharSet].fResult) {
25937                 ++nCharSets;
25938             }
25939         }
25940
25941         if (nCharSets < nb) {
25942             return false;
25943         }
25944         return true;
25945     },
25946     // private
25947     ClientSideStrongPassword: function (pwd)
25948     {
25949         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25950     },
25951     // private
25952     ClientSideMediumPassword: function (pwd)
25953     {
25954         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25955     },
25956     // private
25957     ClientSideWeakPassword: function (pwd)
25958     {
25959         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25960     }
25961           
25962 })//<script type="text/javascript">
25963
25964 /*
25965  * Based  Ext JS Library 1.1.1
25966  * Copyright(c) 2006-2007, Ext JS, LLC.
25967  * LGPL
25968  *
25969  */
25970  
25971 /**
25972  * @class Roo.HtmlEditorCore
25973  * @extends Roo.Component
25974  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25975  *
25976  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25977  */
25978
25979 Roo.HtmlEditorCore = function(config){
25980     
25981     
25982     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25983     
25984     
25985     this.addEvents({
25986         /**
25987          * @event initialize
25988          * Fires when the editor is fully initialized (including the iframe)
25989          * @param {Roo.HtmlEditorCore} this
25990          */
25991         initialize: true,
25992         /**
25993          * @event activate
25994          * Fires when the editor is first receives the focus. Any insertion must wait
25995          * until after this event.
25996          * @param {Roo.HtmlEditorCore} this
25997          */
25998         activate: true,
25999          /**
26000          * @event beforesync
26001          * Fires before the textarea is updated with content from the editor iframe. Return false
26002          * to cancel the sync.
26003          * @param {Roo.HtmlEditorCore} this
26004          * @param {String} html
26005          */
26006         beforesync: true,
26007          /**
26008          * @event beforepush
26009          * Fires before the iframe editor is updated with content from the textarea. Return false
26010          * to cancel the push.
26011          * @param {Roo.HtmlEditorCore} this
26012          * @param {String} html
26013          */
26014         beforepush: true,
26015          /**
26016          * @event sync
26017          * Fires when the textarea is updated with content from the editor iframe.
26018          * @param {Roo.HtmlEditorCore} this
26019          * @param {String} html
26020          */
26021         sync: true,
26022          /**
26023          * @event push
26024          * Fires when the iframe editor is updated with content from the textarea.
26025          * @param {Roo.HtmlEditorCore} this
26026          * @param {String} html
26027          */
26028         push: true,
26029         
26030         /**
26031          * @event editorevent
26032          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26033          * @param {Roo.HtmlEditorCore} this
26034          */
26035         editorevent: true
26036         
26037     });
26038     
26039     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26040     
26041     // defaults : white / black...
26042     this.applyBlacklists();
26043     
26044     
26045     
26046 };
26047
26048
26049 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
26050
26051
26052      /**
26053      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
26054      */
26055     
26056     owner : false,
26057     
26058      /**
26059      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26060      *                        Roo.resizable.
26061      */
26062     resizable : false,
26063      /**
26064      * @cfg {Number} height (in pixels)
26065      */   
26066     height: 300,
26067    /**
26068      * @cfg {Number} width (in pixels)
26069      */   
26070     width: 500,
26071     
26072     /**
26073      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26074      * 
26075      */
26076     stylesheets: false,
26077     
26078     /**
26079      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26080      */
26081     allowComments: false,
26082     // id of frame..
26083     frameId: false,
26084     
26085     // private properties
26086     validationEvent : false,
26087     deferHeight: true,
26088     initialized : false,
26089     activated : false,
26090     sourceEditMode : false,
26091     onFocus : Roo.emptyFn,
26092     iframePad:3,
26093     hideMode:'offsets',
26094     
26095     clearUp: true,
26096     
26097     // blacklist + whitelisted elements..
26098     black: false,
26099     white: false,
26100      
26101     bodyCls : '',
26102
26103     /**
26104      * Protected method that will not generally be called directly. It
26105      * is called when the editor initializes the iframe with HTML contents. Override this method if you
26106      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26107      */
26108     getDocMarkup : function(){
26109         // body styles..
26110         var st = '';
26111         
26112         // inherit styels from page...?? 
26113         if (this.stylesheets === false) {
26114             
26115             Roo.get(document.head).select('style').each(function(node) {
26116                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26117             });
26118             
26119             Roo.get(document.head).select('link').each(function(node) { 
26120                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26121             });
26122             
26123         } else if (!this.stylesheets.length) {
26124                 // simple..
26125                 st = '<style type="text/css">' +
26126                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26127                    '</style>';
26128         } else {
26129             for (var i in this.stylesheets) { 
26130                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26131             }
26132             
26133         }
26134         
26135         st +=  '<style type="text/css">' +
26136             'IMG { cursor: pointer } ' +
26137         '</style>';
26138
26139         var cls = 'roo-htmleditor-body';
26140         
26141         if(this.bodyCls.length){
26142             cls += ' ' + this.bodyCls;
26143         }
26144         
26145         return '<html><head>' + st  +
26146             //<style type="text/css">' +
26147             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26148             //'</style>' +
26149             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
26150     },
26151
26152     // private
26153     onRender : function(ct, position)
26154     {
26155         var _t = this;
26156         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26157         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26158         
26159         
26160         this.el.dom.style.border = '0 none';
26161         this.el.dom.setAttribute('tabIndex', -1);
26162         this.el.addClass('x-hidden hide');
26163         
26164         
26165         
26166         if(Roo.isIE){ // fix IE 1px bogus margin
26167             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26168         }
26169        
26170         
26171         this.frameId = Roo.id();
26172         
26173          
26174         
26175         var iframe = this.owner.wrap.createChild({
26176             tag: 'iframe',
26177             cls: 'form-control', // bootstrap..
26178             id: this.frameId,
26179             name: this.frameId,
26180             frameBorder : 'no',
26181             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
26182         }, this.el
26183         );
26184         
26185         
26186         this.iframe = iframe.dom;
26187
26188          this.assignDocWin();
26189         
26190         this.doc.designMode = 'on';
26191        
26192         this.doc.open();
26193         this.doc.write(this.getDocMarkup());
26194         this.doc.close();
26195
26196         
26197         var task = { // must defer to wait for browser to be ready
26198             run : function(){
26199                 //console.log("run task?" + this.doc.readyState);
26200                 this.assignDocWin();
26201                 if(this.doc.body || this.doc.readyState == 'complete'){
26202                     try {
26203                         this.doc.designMode="on";
26204                     } catch (e) {
26205                         return;
26206                     }
26207                     Roo.TaskMgr.stop(task);
26208                     this.initEditor.defer(10, this);
26209                 }
26210             },
26211             interval : 10,
26212             duration: 10000,
26213             scope: this
26214         };
26215         Roo.TaskMgr.start(task);
26216
26217     },
26218
26219     // private
26220     onResize : function(w, h)
26221     {
26222          Roo.log('resize: ' +w + ',' + h );
26223         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26224         if(!this.iframe){
26225             return;
26226         }
26227         if(typeof w == 'number'){
26228             
26229             this.iframe.style.width = w + 'px';
26230         }
26231         if(typeof h == 'number'){
26232             
26233             this.iframe.style.height = h + 'px';
26234             if(this.doc){
26235                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26236             }
26237         }
26238         
26239     },
26240
26241     /**
26242      * Toggles the editor between standard and source edit mode.
26243      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26244      */
26245     toggleSourceEdit : function(sourceEditMode){
26246         
26247         this.sourceEditMode = sourceEditMode === true;
26248         
26249         if(this.sourceEditMode){
26250  
26251             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
26252             
26253         }else{
26254             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26255             //this.iframe.className = '';
26256             this.deferFocus();
26257         }
26258         //this.setSize(this.owner.wrap.getSize());
26259         //this.fireEvent('editmodechange', this, this.sourceEditMode);
26260     },
26261
26262     
26263   
26264
26265     /**
26266      * Protected method that will not generally be called directly. If you need/want
26267      * custom HTML cleanup, this is the method you should override.
26268      * @param {String} html The HTML to be cleaned
26269      * return {String} The cleaned HTML
26270      */
26271     cleanHtml : function(html){
26272         html = String(html);
26273         if(html.length > 5){
26274             if(Roo.isSafari){ // strip safari nonsense
26275                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26276             }
26277         }
26278         if(html == '&nbsp;'){
26279             html = '';
26280         }
26281         return html;
26282     },
26283
26284     /**
26285      * HTML Editor -> Textarea
26286      * Protected method that will not generally be called directly. Syncs the contents
26287      * of the editor iframe with the textarea.
26288      */
26289     syncValue : function(){
26290         if(this.initialized){
26291             var bd = (this.doc.body || this.doc.documentElement);
26292             //this.cleanUpPaste(); -- this is done else where and causes havoc..
26293             var html = bd.innerHTML;
26294             if(Roo.isSafari){
26295                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26296                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26297                 if(m && m[1]){
26298                     html = '<div style="'+m[0]+'">' + html + '</div>';
26299                 }
26300             }
26301             html = this.cleanHtml(html);
26302             // fix up the special chars.. normaly like back quotes in word...
26303             // however we do not want to do this with chinese..
26304             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26305                 
26306                 var cc = match.charCodeAt();
26307
26308                 // Get the character value, handling surrogate pairs
26309                 if (match.length == 2) {
26310                     // It's a surrogate pair, calculate the Unicode code point
26311                     var high = match.charCodeAt(0) - 0xD800;
26312                     var low  = match.charCodeAt(1) - 0xDC00;
26313                     cc = (high * 0x400) + low + 0x10000;
26314                 }  else if (
26315                     (cc >= 0x4E00 && cc < 0xA000 ) ||
26316                     (cc >= 0x3400 && cc < 0x4E00 ) ||
26317                     (cc >= 0xf900 && cc < 0xfb00 )
26318                 ) {
26319                         return match;
26320                 }  
26321          
26322                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26323                 return "&#" + cc + ";";
26324                 
26325                 
26326             });
26327             
26328             
26329              
26330             if(this.owner.fireEvent('beforesync', this, html) !== false){
26331                 this.el.dom.value = html;
26332                 this.owner.fireEvent('sync', this, html);
26333             }
26334         }
26335     },
26336
26337     /**
26338      * Protected method that will not generally be called directly. Pushes the value of the textarea
26339      * into the iframe editor.
26340      */
26341     pushValue : function(){
26342         if(this.initialized){
26343             var v = this.el.dom.value.trim();
26344             
26345 //            if(v.length < 1){
26346 //                v = '&#160;';
26347 //            }
26348             
26349             if(this.owner.fireEvent('beforepush', this, v) !== false){
26350                 var d = (this.doc.body || this.doc.documentElement);
26351                 d.innerHTML = v;
26352                 this.cleanUpPaste();
26353                 this.el.dom.value = d.innerHTML;
26354                 this.owner.fireEvent('push', this, v);
26355             }
26356         }
26357     },
26358
26359     // private
26360     deferFocus : function(){
26361         this.focus.defer(10, this);
26362     },
26363
26364     // doc'ed in Field
26365     focus : function(){
26366         if(this.win && !this.sourceEditMode){
26367             this.win.focus();
26368         }else{
26369             this.el.focus();
26370         }
26371     },
26372     
26373     assignDocWin: function()
26374     {
26375         var iframe = this.iframe;
26376         
26377          if(Roo.isIE){
26378             this.doc = iframe.contentWindow.document;
26379             this.win = iframe.contentWindow;
26380         } else {
26381 //            if (!Roo.get(this.frameId)) {
26382 //                return;
26383 //            }
26384 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26385 //            this.win = Roo.get(this.frameId).dom.contentWindow;
26386             
26387             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26388                 return;
26389             }
26390             
26391             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26392             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26393         }
26394     },
26395     
26396     // private
26397     initEditor : function(){
26398         //console.log("INIT EDITOR");
26399         this.assignDocWin();
26400         
26401         
26402         
26403         this.doc.designMode="on";
26404         this.doc.open();
26405         this.doc.write(this.getDocMarkup());
26406         this.doc.close();
26407         
26408         var dbody = (this.doc.body || this.doc.documentElement);
26409         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26410         // this copies styles from the containing element into thsi one..
26411         // not sure why we need all of this..
26412         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26413         
26414         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26415         //ss['background-attachment'] = 'fixed'; // w3c
26416         dbody.bgProperties = 'fixed'; // ie
26417         //Roo.DomHelper.applyStyles(dbody, ss);
26418         Roo.EventManager.on(this.doc, {
26419             //'mousedown': this.onEditorEvent,
26420             'mouseup': this.onEditorEvent,
26421             'dblclick': this.onEditorEvent,
26422             'click': this.onEditorEvent,
26423             'keyup': this.onEditorEvent,
26424             buffer:100,
26425             scope: this
26426         });
26427         if(Roo.isGecko){
26428             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26429         }
26430         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26431             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26432         }
26433         this.initialized = true;
26434
26435         this.owner.fireEvent('initialize', this);
26436         this.pushValue();
26437     },
26438
26439     // private
26440     onDestroy : function(){
26441         
26442         
26443         
26444         if(this.rendered){
26445             
26446             //for (var i =0; i < this.toolbars.length;i++) {
26447             //    // fixme - ask toolbars for heights?
26448             //    this.toolbars[i].onDestroy();
26449            // }
26450             
26451             //this.wrap.dom.innerHTML = '';
26452             //this.wrap.remove();
26453         }
26454     },
26455
26456     // private
26457     onFirstFocus : function(){
26458         
26459         this.assignDocWin();
26460         
26461         
26462         this.activated = true;
26463          
26464     
26465         if(Roo.isGecko){ // prevent silly gecko errors
26466             this.win.focus();
26467             var s = this.win.getSelection();
26468             if(!s.focusNode || s.focusNode.nodeType != 3){
26469                 var r = s.getRangeAt(0);
26470                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26471                 r.collapse(true);
26472                 this.deferFocus();
26473             }
26474             try{
26475                 this.execCmd('useCSS', true);
26476                 this.execCmd('styleWithCSS', false);
26477             }catch(e){}
26478         }
26479         this.owner.fireEvent('activate', this);
26480     },
26481
26482     // private
26483     adjustFont: function(btn){
26484         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26485         //if(Roo.isSafari){ // safari
26486         //    adjust *= 2;
26487        // }
26488         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26489         if(Roo.isSafari){ // safari
26490             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26491             v =  (v < 10) ? 10 : v;
26492             v =  (v > 48) ? 48 : v;
26493             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26494             
26495         }
26496         
26497         
26498         v = Math.max(1, v+adjust);
26499         
26500         this.execCmd('FontSize', v  );
26501     },
26502
26503     onEditorEvent : function(e)
26504     {
26505         this.owner.fireEvent('editorevent', this, e);
26506       //  this.updateToolbar();
26507         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26508     },
26509
26510     insertTag : function(tg)
26511     {
26512         // could be a bit smarter... -> wrap the current selected tRoo..
26513         if (tg.toLowerCase() == 'span' ||
26514             tg.toLowerCase() == 'code' ||
26515             tg.toLowerCase() == 'sup' ||
26516             tg.toLowerCase() == 'sub' 
26517             ) {
26518             
26519             range = this.createRange(this.getSelection());
26520             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26521             wrappingNode.appendChild(range.extractContents());
26522             range.insertNode(wrappingNode);
26523
26524             return;
26525             
26526             
26527             
26528         }
26529         this.execCmd("formatblock",   tg);
26530         
26531     },
26532     
26533     insertText : function(txt)
26534     {
26535         
26536         
26537         var range = this.createRange();
26538         range.deleteContents();
26539                //alert(Sender.getAttribute('label'));
26540                
26541         range.insertNode(this.doc.createTextNode(txt));
26542     } ,
26543     
26544      
26545
26546     /**
26547      * Executes a Midas editor command on the editor document and performs necessary focus and
26548      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26549      * @param {String} cmd The Midas command
26550      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26551      */
26552     relayCmd : function(cmd, value){
26553         this.win.focus();
26554         this.execCmd(cmd, value);
26555         this.owner.fireEvent('editorevent', this);
26556         //this.updateToolbar();
26557         this.owner.deferFocus();
26558     },
26559
26560     /**
26561      * Executes a Midas editor command directly on the editor document.
26562      * For visual commands, you should use {@link #relayCmd} instead.
26563      * <b>This should only be called after the editor is initialized.</b>
26564      * @param {String} cmd The Midas command
26565      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26566      */
26567     execCmd : function(cmd, value){
26568         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26569         this.syncValue();
26570     },
26571  
26572  
26573    
26574     /**
26575      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26576      * to insert tRoo.
26577      * @param {String} text | dom node.. 
26578      */
26579     insertAtCursor : function(text)
26580     {
26581         
26582         if(!this.activated){
26583             return;
26584         }
26585         /*
26586         if(Roo.isIE){
26587             this.win.focus();
26588             var r = this.doc.selection.createRange();
26589             if(r){
26590                 r.collapse(true);
26591                 r.pasteHTML(text);
26592                 this.syncValue();
26593                 this.deferFocus();
26594             
26595             }
26596             return;
26597         }
26598         */
26599         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26600             this.win.focus();
26601             
26602             
26603             // from jquery ui (MIT licenced)
26604             var range, node;
26605             var win = this.win;
26606             
26607             if (win.getSelection && win.getSelection().getRangeAt) {
26608                 range = win.getSelection().getRangeAt(0);
26609                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26610                 range.insertNode(node);
26611             } else if (win.document.selection && win.document.selection.createRange) {
26612                 // no firefox support
26613                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26614                 win.document.selection.createRange().pasteHTML(txt);
26615             } else {
26616                 // no firefox support
26617                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26618                 this.execCmd('InsertHTML', txt);
26619             } 
26620             
26621             this.syncValue();
26622             
26623             this.deferFocus();
26624         }
26625     },
26626  // private
26627     mozKeyPress : function(e){
26628         if(e.ctrlKey){
26629             var c = e.getCharCode(), cmd;
26630           
26631             if(c > 0){
26632                 c = String.fromCharCode(c).toLowerCase();
26633                 switch(c){
26634                     case 'b':
26635                         cmd = 'bold';
26636                         break;
26637                     case 'i':
26638                         cmd = 'italic';
26639                         break;
26640                     
26641                     case 'u':
26642                         cmd = 'underline';
26643                         break;
26644                     
26645                     case 'v':
26646                         this.cleanUpPaste.defer(100, this);
26647                         return;
26648                         
26649                 }
26650                 if(cmd){
26651                     this.win.focus();
26652                     this.execCmd(cmd);
26653                     this.deferFocus();
26654                     e.preventDefault();
26655                 }
26656                 
26657             }
26658         }
26659     },
26660
26661     // private
26662     fixKeys : function(){ // load time branching for fastest keydown performance
26663         if(Roo.isIE){
26664             return function(e){
26665                 var k = e.getKey(), r;
26666                 if(k == e.TAB){
26667                     e.stopEvent();
26668                     r = this.doc.selection.createRange();
26669                     if(r){
26670                         r.collapse(true);
26671                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26672                         this.deferFocus();
26673                     }
26674                     return;
26675                 }
26676                 
26677                 if(k == e.ENTER){
26678                     r = this.doc.selection.createRange();
26679                     if(r){
26680                         var target = r.parentElement();
26681                         if(!target || target.tagName.toLowerCase() != 'li'){
26682                             e.stopEvent();
26683                             r.pasteHTML('<br />');
26684                             r.collapse(false);
26685                             r.select();
26686                         }
26687                     }
26688                 }
26689                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26690                     this.cleanUpPaste.defer(100, this);
26691                     return;
26692                 }
26693                 
26694                 
26695             };
26696         }else if(Roo.isOpera){
26697             return function(e){
26698                 var k = e.getKey();
26699                 if(k == e.TAB){
26700                     e.stopEvent();
26701                     this.win.focus();
26702                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26703                     this.deferFocus();
26704                 }
26705                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26706                     this.cleanUpPaste.defer(100, this);
26707                     return;
26708                 }
26709                 
26710             };
26711         }else if(Roo.isSafari){
26712             return function(e){
26713                 var k = e.getKey();
26714                 
26715                 if(k == e.TAB){
26716                     e.stopEvent();
26717                     this.execCmd('InsertText','\t');
26718                     this.deferFocus();
26719                     return;
26720                 }
26721                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26722                     this.cleanUpPaste.defer(100, this);
26723                     return;
26724                 }
26725                 
26726              };
26727         }
26728     }(),
26729     
26730     getAllAncestors: function()
26731     {
26732         var p = this.getSelectedNode();
26733         var a = [];
26734         if (!p) {
26735             a.push(p); // push blank onto stack..
26736             p = this.getParentElement();
26737         }
26738         
26739         
26740         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26741             a.push(p);
26742             p = p.parentNode;
26743         }
26744         a.push(this.doc.body);
26745         return a;
26746     },
26747     lastSel : false,
26748     lastSelNode : false,
26749     
26750     
26751     getSelection : function() 
26752     {
26753         this.assignDocWin();
26754         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26755     },
26756     
26757     getSelectedNode: function() 
26758     {
26759         // this may only work on Gecko!!!
26760         
26761         // should we cache this!!!!
26762         
26763         
26764         
26765          
26766         var range = this.createRange(this.getSelection()).cloneRange();
26767         
26768         if (Roo.isIE) {
26769             var parent = range.parentElement();
26770             while (true) {
26771                 var testRange = range.duplicate();
26772                 testRange.moveToElementText(parent);
26773                 if (testRange.inRange(range)) {
26774                     break;
26775                 }
26776                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26777                     break;
26778                 }
26779                 parent = parent.parentElement;
26780             }
26781             return parent;
26782         }
26783         
26784         // is ancestor a text element.
26785         var ac =  range.commonAncestorContainer;
26786         if (ac.nodeType == 3) {
26787             ac = ac.parentNode;
26788         }
26789         
26790         var ar = ac.childNodes;
26791          
26792         var nodes = [];
26793         var other_nodes = [];
26794         var has_other_nodes = false;
26795         for (var i=0;i<ar.length;i++) {
26796             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26797                 continue;
26798             }
26799             // fullly contained node.
26800             
26801             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26802                 nodes.push(ar[i]);
26803                 continue;
26804             }
26805             
26806             // probably selected..
26807             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26808                 other_nodes.push(ar[i]);
26809                 continue;
26810             }
26811             // outer..
26812             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26813                 continue;
26814             }
26815             
26816             
26817             has_other_nodes = true;
26818         }
26819         if (!nodes.length && other_nodes.length) {
26820             nodes= other_nodes;
26821         }
26822         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26823             return false;
26824         }
26825         
26826         return nodes[0];
26827     },
26828     createRange: function(sel)
26829     {
26830         // this has strange effects when using with 
26831         // top toolbar - not sure if it's a great idea.
26832         //this.editor.contentWindow.focus();
26833         if (typeof sel != "undefined") {
26834             try {
26835                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26836             } catch(e) {
26837                 return this.doc.createRange();
26838             }
26839         } else {
26840             return this.doc.createRange();
26841         }
26842     },
26843     getParentElement: function()
26844     {
26845         
26846         this.assignDocWin();
26847         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26848         
26849         var range = this.createRange(sel);
26850          
26851         try {
26852             var p = range.commonAncestorContainer;
26853             while (p.nodeType == 3) { // text node
26854                 p = p.parentNode;
26855             }
26856             return p;
26857         } catch (e) {
26858             return null;
26859         }
26860     
26861     },
26862     /***
26863      *
26864      * Range intersection.. the hard stuff...
26865      *  '-1' = before
26866      *  '0' = hits..
26867      *  '1' = after.
26868      *         [ -- selected range --- ]
26869      *   [fail]                        [fail]
26870      *
26871      *    basically..
26872      *      if end is before start or  hits it. fail.
26873      *      if start is after end or hits it fail.
26874      *
26875      *   if either hits (but other is outside. - then it's not 
26876      *   
26877      *    
26878      **/
26879     
26880     
26881     // @see http://www.thismuchiknow.co.uk/?p=64.
26882     rangeIntersectsNode : function(range, node)
26883     {
26884         var nodeRange = node.ownerDocument.createRange();
26885         try {
26886             nodeRange.selectNode(node);
26887         } catch (e) {
26888             nodeRange.selectNodeContents(node);
26889         }
26890     
26891         var rangeStartRange = range.cloneRange();
26892         rangeStartRange.collapse(true);
26893     
26894         var rangeEndRange = range.cloneRange();
26895         rangeEndRange.collapse(false);
26896     
26897         var nodeStartRange = nodeRange.cloneRange();
26898         nodeStartRange.collapse(true);
26899     
26900         var nodeEndRange = nodeRange.cloneRange();
26901         nodeEndRange.collapse(false);
26902     
26903         return rangeStartRange.compareBoundaryPoints(
26904                  Range.START_TO_START, nodeEndRange) == -1 &&
26905                rangeEndRange.compareBoundaryPoints(
26906                  Range.START_TO_START, nodeStartRange) == 1;
26907         
26908          
26909     },
26910     rangeCompareNode : function(range, node)
26911     {
26912         var nodeRange = node.ownerDocument.createRange();
26913         try {
26914             nodeRange.selectNode(node);
26915         } catch (e) {
26916             nodeRange.selectNodeContents(node);
26917         }
26918         
26919         
26920         range.collapse(true);
26921     
26922         nodeRange.collapse(true);
26923      
26924         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26925         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26926          
26927         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26928         
26929         var nodeIsBefore   =  ss == 1;
26930         var nodeIsAfter    = ee == -1;
26931         
26932         if (nodeIsBefore && nodeIsAfter) {
26933             return 0; // outer
26934         }
26935         if (!nodeIsBefore && nodeIsAfter) {
26936             return 1; //right trailed.
26937         }
26938         
26939         if (nodeIsBefore && !nodeIsAfter) {
26940             return 2;  // left trailed.
26941         }
26942         // fully contined.
26943         return 3;
26944     },
26945
26946     // private? - in a new class?
26947     cleanUpPaste :  function()
26948     {
26949         // cleans up the whole document..
26950         Roo.log('cleanuppaste');
26951         
26952         this.cleanUpChildren(this.doc.body);
26953         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26954         if (clean != this.doc.body.innerHTML) {
26955             this.doc.body.innerHTML = clean;
26956         }
26957         
26958     },
26959     
26960     cleanWordChars : function(input) {// change the chars to hex code
26961         var he = Roo.HtmlEditorCore;
26962         
26963         var output = input;
26964         Roo.each(he.swapCodes, function(sw) { 
26965             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26966             
26967             output = output.replace(swapper, sw[1]);
26968         });
26969         
26970         return output;
26971     },
26972     
26973     
26974     cleanUpChildren : function (n)
26975     {
26976         if (!n.childNodes.length) {
26977             return;
26978         }
26979         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26980            this.cleanUpChild(n.childNodes[i]);
26981         }
26982     },
26983     
26984     
26985         
26986     
26987     cleanUpChild : function (node)
26988     {
26989         var ed = this;
26990         //console.log(node);
26991         if (node.nodeName == "#text") {
26992             // clean up silly Windows -- stuff?
26993             return; 
26994         }
26995         if (node.nodeName == "#comment") {
26996             if (!this.allowComments) {
26997                 node.parentNode.removeChild(node);
26998             }
26999             // clean up silly Windows -- stuff?
27000             return; 
27001         }
27002         var lcname = node.tagName.toLowerCase();
27003         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27004         // whitelist of tags..
27005         
27006         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27007             // remove node.
27008             node.parentNode.removeChild(node);
27009             return;
27010             
27011         }
27012         
27013         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27014         
27015         // spans with no attributes - just remove them..
27016         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
27017             remove_keep_children = true;
27018         }
27019         
27020         // remove <a name=....> as rendering on yahoo mailer is borked with this.
27021         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27022         
27023         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27024         //    remove_keep_children = true;
27025         //}
27026         
27027         if (remove_keep_children) {
27028             this.cleanUpChildren(node);
27029             // inserts everything just before this node...
27030             while (node.childNodes.length) {
27031                 var cn = node.childNodes[0];
27032                 node.removeChild(cn);
27033                 node.parentNode.insertBefore(cn, node);
27034             }
27035             node.parentNode.removeChild(node);
27036             return;
27037         }
27038         
27039         if (!node.attributes || !node.attributes.length) {
27040             
27041           
27042             
27043             
27044             this.cleanUpChildren(node);
27045             return;
27046         }
27047         
27048         function cleanAttr(n,v)
27049         {
27050             
27051             if (v.match(/^\./) || v.match(/^\//)) {
27052                 return;
27053             }
27054             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27055                 return;
27056             }
27057             if (v.match(/^#/)) {
27058                 return;
27059             }
27060             if (v.match(/^\{/)) { // allow template editing.
27061                 return;
27062             }
27063 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27064             node.removeAttribute(n);
27065             
27066         }
27067         
27068         var cwhite = this.cwhite;
27069         var cblack = this.cblack;
27070             
27071         function cleanStyle(n,v)
27072         {
27073             if (v.match(/expression/)) { //XSS?? should we even bother..
27074                 node.removeAttribute(n);
27075                 return;
27076             }
27077             
27078             var parts = v.split(/;/);
27079             var clean = [];
27080             
27081             Roo.each(parts, function(p) {
27082                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27083                 if (!p.length) {
27084                     return true;
27085                 }
27086                 var l = p.split(':').shift().replace(/\s+/g,'');
27087                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27088                 
27089                 if ( cwhite.length && cblack.indexOf(l) > -1) {
27090 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27091                     //node.removeAttribute(n);
27092                     return true;
27093                 }
27094                 //Roo.log()
27095                 // only allow 'c whitelisted system attributes'
27096                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
27097 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27098                     //node.removeAttribute(n);
27099                     return true;
27100                 }
27101                 
27102                 
27103                  
27104                 
27105                 clean.push(p);
27106                 return true;
27107             });
27108             if (clean.length) { 
27109                 node.setAttribute(n, clean.join(';'));
27110             } else {
27111                 node.removeAttribute(n);
27112             }
27113             
27114         }
27115         
27116         
27117         for (var i = node.attributes.length-1; i > -1 ; i--) {
27118             var a = node.attributes[i];
27119             //console.log(a);
27120             
27121             if (a.name.toLowerCase().substr(0,2)=='on')  {
27122                 node.removeAttribute(a.name);
27123                 continue;
27124             }
27125             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27126                 node.removeAttribute(a.name);
27127                 continue;
27128             }
27129             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27130                 cleanAttr(a.name,a.value); // fixme..
27131                 continue;
27132             }
27133             if (a.name == 'style') {
27134                 cleanStyle(a.name,a.value);
27135                 continue;
27136             }
27137             /// clean up MS crap..
27138             // tecnically this should be a list of valid class'es..
27139             
27140             
27141             if (a.name == 'class') {
27142                 if (a.value.match(/^Mso/)) {
27143                     node.removeAttribute('class');
27144                 }
27145                 
27146                 if (a.value.match(/^body$/)) {
27147                     node.removeAttribute('class');
27148                 }
27149                 continue;
27150             }
27151             
27152             // style cleanup!?
27153             // class cleanup?
27154             
27155         }
27156         
27157         
27158         this.cleanUpChildren(node);
27159         
27160         
27161     },
27162     
27163     /**
27164      * Clean up MS wordisms...
27165      */
27166     cleanWord : function(node)
27167     {
27168         if (!node) {
27169             this.cleanWord(this.doc.body);
27170             return;
27171         }
27172         
27173         if(
27174                 node.nodeName == 'SPAN' &&
27175                 !node.hasAttributes() &&
27176                 node.childNodes.length == 1 &&
27177                 node.firstChild.nodeName == "#text"  
27178         ) {
27179             var textNode = node.firstChild;
27180             node.removeChild(textNode);
27181             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27182                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27183             }
27184             node.parentNode.insertBefore(textNode, node);
27185             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
27186                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27187             }
27188             node.parentNode.removeChild(node);
27189         }
27190         
27191         if (node.nodeName == "#text") {
27192             // clean up silly Windows -- stuff?
27193             return; 
27194         }
27195         if (node.nodeName == "#comment") {
27196             node.parentNode.removeChild(node);
27197             // clean up silly Windows -- stuff?
27198             return; 
27199         }
27200         
27201         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27202             node.parentNode.removeChild(node);
27203             return;
27204         }
27205         //Roo.log(node.tagName);
27206         // remove - but keep children..
27207         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27208             //Roo.log('-- removed');
27209             while (node.childNodes.length) {
27210                 var cn = node.childNodes[0];
27211                 node.removeChild(cn);
27212                 node.parentNode.insertBefore(cn, node);
27213                 // move node to parent - and clean it..
27214                 this.cleanWord(cn);
27215             }
27216             node.parentNode.removeChild(node);
27217             /// no need to iterate chidlren = it's got none..
27218             //this.iterateChildren(node, this.cleanWord);
27219             return;
27220         }
27221         // clean styles
27222         if (node.className.length) {
27223             
27224             var cn = node.className.split(/\W+/);
27225             var cna = [];
27226             Roo.each(cn, function(cls) {
27227                 if (cls.match(/Mso[a-zA-Z]+/)) {
27228                     return;
27229                 }
27230                 cna.push(cls);
27231             });
27232             node.className = cna.length ? cna.join(' ') : '';
27233             if (!cna.length) {
27234                 node.removeAttribute("class");
27235             }
27236         }
27237         
27238         if (node.hasAttribute("lang")) {
27239             node.removeAttribute("lang");
27240         }
27241         
27242         if (node.hasAttribute("style")) {
27243             
27244             var styles = node.getAttribute("style").split(";");
27245             var nstyle = [];
27246             Roo.each(styles, function(s) {
27247                 if (!s.match(/:/)) {
27248                     return;
27249                 }
27250                 var kv = s.split(":");
27251                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27252                     return;
27253                 }
27254                 // what ever is left... we allow.
27255                 nstyle.push(s);
27256             });
27257             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27258             if (!nstyle.length) {
27259                 node.removeAttribute('style');
27260             }
27261         }
27262         this.iterateChildren(node, this.cleanWord);
27263         
27264         
27265         
27266     },
27267     /**
27268      * iterateChildren of a Node, calling fn each time, using this as the scole..
27269      * @param {DomNode} node node to iterate children of.
27270      * @param {Function} fn method of this class to call on each item.
27271      */
27272     iterateChildren : function(node, fn)
27273     {
27274         if (!node.childNodes.length) {
27275                 return;
27276         }
27277         for (var i = node.childNodes.length-1; i > -1 ; i--) {
27278            fn.call(this, node.childNodes[i])
27279         }
27280     },
27281     
27282     
27283     /**
27284      * cleanTableWidths.
27285      *
27286      * Quite often pasting from word etc.. results in tables with column and widths.
27287      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27288      *
27289      */
27290     cleanTableWidths : function(node)
27291     {
27292          
27293          
27294         if (!node) {
27295             this.cleanTableWidths(this.doc.body);
27296             return;
27297         }
27298         
27299         // ignore list...
27300         if (node.nodeName == "#text" || node.nodeName == "#comment") {
27301             return; 
27302         }
27303         Roo.log(node.tagName);
27304         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27305             this.iterateChildren(node, this.cleanTableWidths);
27306             return;
27307         }
27308         if (node.hasAttribute('width')) {
27309             node.removeAttribute('width');
27310         }
27311         
27312          
27313         if (node.hasAttribute("style")) {
27314             // pretty basic...
27315             
27316             var styles = node.getAttribute("style").split(";");
27317             var nstyle = [];
27318             Roo.each(styles, function(s) {
27319                 if (!s.match(/:/)) {
27320                     return;
27321                 }
27322                 var kv = s.split(":");
27323                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27324                     return;
27325                 }
27326                 // what ever is left... we allow.
27327                 nstyle.push(s);
27328             });
27329             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27330             if (!nstyle.length) {
27331                 node.removeAttribute('style');
27332             }
27333         }
27334         
27335         this.iterateChildren(node, this.cleanTableWidths);
27336         
27337         
27338     },
27339     
27340     
27341     
27342     
27343     domToHTML : function(currentElement, depth, nopadtext) {
27344         
27345         depth = depth || 0;
27346         nopadtext = nopadtext || false;
27347     
27348         if (!currentElement) {
27349             return this.domToHTML(this.doc.body);
27350         }
27351         
27352         //Roo.log(currentElement);
27353         var j;
27354         var allText = false;
27355         var nodeName = currentElement.nodeName;
27356         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27357         
27358         if  (nodeName == '#text') {
27359             
27360             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27361         }
27362         
27363         
27364         var ret = '';
27365         if (nodeName != 'BODY') {
27366              
27367             var i = 0;
27368             // Prints the node tagName, such as <A>, <IMG>, etc
27369             if (tagName) {
27370                 var attr = [];
27371                 for(i = 0; i < currentElement.attributes.length;i++) {
27372                     // quoting?
27373                     var aname = currentElement.attributes.item(i).name;
27374                     if (!currentElement.attributes.item(i).value.length) {
27375                         continue;
27376                     }
27377                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27378                 }
27379                 
27380                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27381             } 
27382             else {
27383                 
27384                 // eack
27385             }
27386         } else {
27387             tagName = false;
27388         }
27389         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27390             return ret;
27391         }
27392         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27393             nopadtext = true;
27394         }
27395         
27396         
27397         // Traverse the tree
27398         i = 0;
27399         var currentElementChild = currentElement.childNodes.item(i);
27400         var allText = true;
27401         var innerHTML  = '';
27402         lastnode = '';
27403         while (currentElementChild) {
27404             // Formatting code (indent the tree so it looks nice on the screen)
27405             var nopad = nopadtext;
27406             if (lastnode == 'SPAN') {
27407                 nopad  = true;
27408             }
27409             // text
27410             if  (currentElementChild.nodeName == '#text') {
27411                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27412                 toadd = nopadtext ? toadd : toadd.trim();
27413                 if (!nopad && toadd.length > 80) {
27414                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
27415                 }
27416                 innerHTML  += toadd;
27417                 
27418                 i++;
27419                 currentElementChild = currentElement.childNodes.item(i);
27420                 lastNode = '';
27421                 continue;
27422             }
27423             allText = false;
27424             
27425             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
27426                 
27427             // Recursively traverse the tree structure of the child node
27428             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
27429             lastnode = currentElementChild.nodeName;
27430             i++;
27431             currentElementChild=currentElement.childNodes.item(i);
27432         }
27433         
27434         ret += innerHTML;
27435         
27436         if (!allText) {
27437                 // The remaining code is mostly for formatting the tree
27438             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
27439         }
27440         
27441         
27442         if (tagName) {
27443             ret+= "</"+tagName+">";
27444         }
27445         return ret;
27446         
27447     },
27448         
27449     applyBlacklists : function()
27450     {
27451         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
27452         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
27453         
27454         this.white = [];
27455         this.black = [];
27456         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27457             if (b.indexOf(tag) > -1) {
27458                 return;
27459             }
27460             this.white.push(tag);
27461             
27462         }, this);
27463         
27464         Roo.each(w, function(tag) {
27465             if (b.indexOf(tag) > -1) {
27466                 return;
27467             }
27468             if (this.white.indexOf(tag) > -1) {
27469                 return;
27470             }
27471             this.white.push(tag);
27472             
27473         }, this);
27474         
27475         
27476         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27477             if (w.indexOf(tag) > -1) {
27478                 return;
27479             }
27480             this.black.push(tag);
27481             
27482         }, this);
27483         
27484         Roo.each(b, function(tag) {
27485             if (w.indexOf(tag) > -1) {
27486                 return;
27487             }
27488             if (this.black.indexOf(tag) > -1) {
27489                 return;
27490             }
27491             this.black.push(tag);
27492             
27493         }, this);
27494         
27495         
27496         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27497         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27498         
27499         this.cwhite = [];
27500         this.cblack = [];
27501         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27502             if (b.indexOf(tag) > -1) {
27503                 return;
27504             }
27505             this.cwhite.push(tag);
27506             
27507         }, this);
27508         
27509         Roo.each(w, function(tag) {
27510             if (b.indexOf(tag) > -1) {
27511                 return;
27512             }
27513             if (this.cwhite.indexOf(tag) > -1) {
27514                 return;
27515             }
27516             this.cwhite.push(tag);
27517             
27518         }, this);
27519         
27520         
27521         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27522             if (w.indexOf(tag) > -1) {
27523                 return;
27524             }
27525             this.cblack.push(tag);
27526             
27527         }, this);
27528         
27529         Roo.each(b, function(tag) {
27530             if (w.indexOf(tag) > -1) {
27531                 return;
27532             }
27533             if (this.cblack.indexOf(tag) > -1) {
27534                 return;
27535             }
27536             this.cblack.push(tag);
27537             
27538         }, this);
27539     },
27540     
27541     setStylesheets : function(stylesheets)
27542     {
27543         if(typeof(stylesheets) == 'string'){
27544             Roo.get(this.iframe.contentDocument.head).createChild({
27545                 tag : 'link',
27546                 rel : 'stylesheet',
27547                 type : 'text/css',
27548                 href : stylesheets
27549             });
27550             
27551             return;
27552         }
27553         var _this = this;
27554      
27555         Roo.each(stylesheets, function(s) {
27556             if(!s.length){
27557                 return;
27558             }
27559             
27560             Roo.get(_this.iframe.contentDocument.head).createChild({
27561                 tag : 'link',
27562                 rel : 'stylesheet',
27563                 type : 'text/css',
27564                 href : s
27565             });
27566         });
27567
27568         
27569     },
27570     
27571     removeStylesheets : function()
27572     {
27573         var _this = this;
27574         
27575         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27576             s.remove();
27577         });
27578     },
27579     
27580     setStyle : function(style)
27581     {
27582         Roo.get(this.iframe.contentDocument.head).createChild({
27583             tag : 'style',
27584             type : 'text/css',
27585             html : style
27586         });
27587
27588         return;
27589     }
27590     
27591     // hide stuff that is not compatible
27592     /**
27593      * @event blur
27594      * @hide
27595      */
27596     /**
27597      * @event change
27598      * @hide
27599      */
27600     /**
27601      * @event focus
27602      * @hide
27603      */
27604     /**
27605      * @event specialkey
27606      * @hide
27607      */
27608     /**
27609      * @cfg {String} fieldClass @hide
27610      */
27611     /**
27612      * @cfg {String} focusClass @hide
27613      */
27614     /**
27615      * @cfg {String} autoCreate @hide
27616      */
27617     /**
27618      * @cfg {String} inputType @hide
27619      */
27620     /**
27621      * @cfg {String} invalidClass @hide
27622      */
27623     /**
27624      * @cfg {String} invalidText @hide
27625      */
27626     /**
27627      * @cfg {String} msgFx @hide
27628      */
27629     /**
27630      * @cfg {String} validateOnBlur @hide
27631      */
27632 });
27633
27634 Roo.HtmlEditorCore.white = [
27635         'area', 'br', 'img', 'input', 'hr', 'wbr',
27636         
27637        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27638        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27639        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27640        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27641        'table',   'ul',         'xmp', 
27642        
27643        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27644       'thead',   'tr', 
27645      
27646       'dir', 'menu', 'ol', 'ul', 'dl',
27647        
27648       'embed',  'object'
27649 ];
27650
27651
27652 Roo.HtmlEditorCore.black = [
27653     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27654         'applet', // 
27655         'base',   'basefont', 'bgsound', 'blink',  'body', 
27656         'frame',  'frameset', 'head',    'html',   'ilayer', 
27657         'iframe', 'layer',  'link',     'meta',    'object',   
27658         'script', 'style' ,'title',  'xml' // clean later..
27659 ];
27660 Roo.HtmlEditorCore.clean = [
27661     'script', 'style', 'title', 'xml'
27662 ];
27663 Roo.HtmlEditorCore.remove = [
27664     'font'
27665 ];
27666 // attributes..
27667
27668 Roo.HtmlEditorCore.ablack = [
27669     'on'
27670 ];
27671     
27672 Roo.HtmlEditorCore.aclean = [ 
27673     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27674 ];
27675
27676 // protocols..
27677 Roo.HtmlEditorCore.pwhite= [
27678         'http',  'https',  'mailto'
27679 ];
27680
27681 // white listed style attributes.
27682 Roo.HtmlEditorCore.cwhite= [
27683       //  'text-align', /// default is to allow most things..
27684       
27685          
27686 //        'font-size'//??
27687 ];
27688
27689 // black listed style attributes.
27690 Roo.HtmlEditorCore.cblack= [
27691       //  'font-size' -- this can be set by the project 
27692 ];
27693
27694
27695 Roo.HtmlEditorCore.swapCodes   =[ 
27696     [    8211, "&#8211;" ], 
27697     [    8212, "&#8212;" ], 
27698     [    8216,  "'" ],  
27699     [    8217, "'" ],  
27700     [    8220, '"' ],  
27701     [    8221, '"' ],  
27702     [    8226, "*" ],  
27703     [    8230, "..." ]
27704 ]; 
27705
27706     /*
27707  * - LGPL
27708  *
27709  * HtmlEditor
27710  * 
27711  */
27712
27713 /**
27714  * @class Roo.bootstrap.form.HtmlEditor
27715  * @extends Roo.bootstrap.form.TextArea
27716  * Bootstrap HtmlEditor class
27717
27718  * @constructor
27719  * Create a new HtmlEditor
27720  * @param {Object} config The config object
27721  */
27722
27723 Roo.bootstrap.form.HtmlEditor = function(config){
27724     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27725     if (!this.toolbars) {
27726         this.toolbars = [];
27727     }
27728     
27729     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27730     this.addEvents({
27731             /**
27732              * @event initialize
27733              * Fires when the editor is fully initialized (including the iframe)
27734              * @param {HtmlEditor} this
27735              */
27736             initialize: true,
27737             /**
27738              * @event activate
27739              * Fires when the editor is first receives the focus. Any insertion must wait
27740              * until after this event.
27741              * @param {HtmlEditor} this
27742              */
27743             activate: true,
27744              /**
27745              * @event beforesync
27746              * Fires before the textarea is updated with content from the editor iframe. Return false
27747              * to cancel the sync.
27748              * @param {HtmlEditor} this
27749              * @param {String} html
27750              */
27751             beforesync: true,
27752              /**
27753              * @event beforepush
27754              * Fires before the iframe editor is updated with content from the textarea. Return false
27755              * to cancel the push.
27756              * @param {HtmlEditor} this
27757              * @param {String} html
27758              */
27759             beforepush: true,
27760              /**
27761              * @event sync
27762              * Fires when the textarea is updated with content from the editor iframe.
27763              * @param {HtmlEditor} this
27764              * @param {String} html
27765              */
27766             sync: true,
27767              /**
27768              * @event push
27769              * Fires when the iframe editor is updated with content from the textarea.
27770              * @param {HtmlEditor} this
27771              * @param {String} html
27772              */
27773             push: true,
27774              /**
27775              * @event editmodechange
27776              * Fires when the editor switches edit modes
27777              * @param {HtmlEditor} this
27778              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27779              */
27780             editmodechange: true,
27781             /**
27782              * @event editorevent
27783              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27784              * @param {HtmlEditor} this
27785              */
27786             editorevent: true,
27787             /**
27788              * @event firstfocus
27789              * Fires when on first focus - needed by toolbars..
27790              * @param {HtmlEditor} this
27791              */
27792             firstfocus: true,
27793             /**
27794              * @event autosave
27795              * Auto save the htmlEditor value as a file into Events
27796              * @param {HtmlEditor} this
27797              */
27798             autosave: true,
27799             /**
27800              * @event savedpreview
27801              * preview the saved version of htmlEditor
27802              * @param {HtmlEditor} this
27803              */
27804             savedpreview: true
27805         });
27806 };
27807
27808
27809 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
27810     
27811     
27812       /**
27813      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27814      */
27815     toolbars : false,
27816     
27817      /**
27818     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27819     */
27820     btns : [],
27821    
27822      /**
27823      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27824      *                        Roo.resizable.
27825      */
27826     resizable : false,
27827      /**
27828      * @cfg {Number} height (in pixels)
27829      */   
27830     height: 300,
27831    /**
27832      * @cfg {Number} width (in pixels)
27833      */   
27834     width: false,
27835     
27836     /**
27837      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27838      * 
27839      */
27840     stylesheets: false,
27841     
27842     // id of frame..
27843     frameId: false,
27844     
27845     // private properties
27846     validationEvent : false,
27847     deferHeight: true,
27848     initialized : false,
27849     activated : false,
27850     
27851     onFocus : Roo.emptyFn,
27852     iframePad:3,
27853     hideMode:'offsets',
27854     
27855     tbContainer : false,
27856     
27857     bodyCls : '',
27858     
27859     toolbarContainer :function() {
27860         return this.wrap.select('.x-html-editor-tb',true).first();
27861     },
27862
27863     /**
27864      * Protected method that will not generally be called directly. It
27865      * is called when the editor creates its toolbar. Override this method if you need to
27866      * add custom toolbar buttons.
27867      * @param {HtmlEditor} editor
27868      */
27869     createToolbar : function(){
27870         Roo.log('renewing');
27871         Roo.log("create toolbars");
27872         
27873         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27874         this.toolbars[0].render(this.toolbarContainer());
27875         
27876         return;
27877         
27878 //        if (!editor.toolbars || !editor.toolbars.length) {
27879 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27880 //        }
27881 //        
27882 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27883 //            editor.toolbars[i] = Roo.factory(
27884 //                    typeof(editor.toolbars[i]) == 'string' ?
27885 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27886 //                Roo.bootstrap.form.HtmlEditor);
27887 //            editor.toolbars[i].init(editor);
27888 //        }
27889     },
27890
27891      
27892     // private
27893     onRender : function(ct, position)
27894     {
27895        // Roo.log("Call onRender: " + this.xtype);
27896         var _t = this;
27897         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27898       
27899         this.wrap = this.inputEl().wrap({
27900             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27901         });
27902         
27903         this.editorcore.onRender(ct, position);
27904          
27905         if (this.resizable) {
27906             this.resizeEl = new Roo.Resizable(this.wrap, {
27907                 pinned : true,
27908                 wrap: true,
27909                 dynamic : true,
27910                 minHeight : this.height,
27911                 height: this.height,
27912                 handles : this.resizable,
27913                 width: this.width,
27914                 listeners : {
27915                     resize : function(r, w, h) {
27916                         _t.onResize(w,h); // -something
27917                     }
27918                 }
27919             });
27920             
27921         }
27922         this.createToolbar(this);
27923        
27924         
27925         if(!this.width && this.resizable){
27926             this.setSize(this.wrap.getSize());
27927         }
27928         if (this.resizeEl) {
27929             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27930             // should trigger onReize..
27931         }
27932         
27933     },
27934
27935     // private
27936     onResize : function(w, h)
27937     {
27938         Roo.log('resize: ' +w + ',' + h );
27939         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27940         var ew = false;
27941         var eh = false;
27942         
27943         if(this.inputEl() ){
27944             if(typeof w == 'number'){
27945                 var aw = w - this.wrap.getFrameWidth('lr');
27946                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27947                 ew = aw;
27948             }
27949             if(typeof h == 'number'){
27950                  var tbh = -11;  // fixme it needs to tool bar size!
27951                 for (var i =0; i < this.toolbars.length;i++) {
27952                     // fixme - ask toolbars for heights?
27953                     tbh += this.toolbars[i].el.getHeight();
27954                     //if (this.toolbars[i].footer) {
27955                     //    tbh += this.toolbars[i].footer.el.getHeight();
27956                     //}
27957                 }
27958               
27959                 
27960                 
27961                 
27962                 
27963                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27964                 ah -= 5; // knock a few pixes off for look..
27965                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27966                 var eh = ah;
27967             }
27968         }
27969         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27970         this.editorcore.onResize(ew,eh);
27971         
27972     },
27973
27974     /**
27975      * Toggles the editor between standard and source edit mode.
27976      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27977      */
27978     toggleSourceEdit : function(sourceEditMode)
27979     {
27980         this.editorcore.toggleSourceEdit(sourceEditMode);
27981         
27982         if(this.editorcore.sourceEditMode){
27983             Roo.log('editor - showing textarea');
27984             
27985 //            Roo.log('in');
27986 //            Roo.log(this.syncValue());
27987             this.syncValue();
27988             this.inputEl().removeClass(['hide', 'x-hidden']);
27989             this.inputEl().dom.removeAttribute('tabIndex');
27990             this.inputEl().focus();
27991         }else{
27992             Roo.log('editor - hiding textarea');
27993 //            Roo.log('out')
27994 //            Roo.log(this.pushValue()); 
27995             this.pushValue();
27996             
27997             this.inputEl().addClass(['hide', 'x-hidden']);
27998             this.inputEl().dom.setAttribute('tabIndex', -1);
27999             //this.deferFocus();
28000         }
28001          
28002         if(this.resizable){
28003             this.setSize(this.wrap.getSize());
28004         }
28005         
28006         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28007     },
28008  
28009     // private (for BoxComponent)
28010     adjustSize : Roo.BoxComponent.prototype.adjustSize,
28011
28012     // private (for BoxComponent)
28013     getResizeEl : function(){
28014         return this.wrap;
28015     },
28016
28017     // private (for BoxComponent)
28018     getPositionEl : function(){
28019         return this.wrap;
28020     },
28021
28022     // private
28023     initEvents : function(){
28024         this.originalValue = this.getValue();
28025     },
28026
28027 //    /**
28028 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28029 //     * @method
28030 //     */
28031 //    markInvalid : Roo.emptyFn,
28032 //    /**
28033 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28034 //     * @method
28035 //     */
28036 //    clearInvalid : Roo.emptyFn,
28037
28038     setValue : function(v){
28039         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28040         this.editorcore.pushValue();
28041     },
28042
28043      
28044     // private
28045     deferFocus : function(){
28046         this.focus.defer(10, this);
28047     },
28048
28049     // doc'ed in Field
28050     focus : function(){
28051         this.editorcore.focus();
28052         
28053     },
28054       
28055
28056     // private
28057     onDestroy : function(){
28058         
28059         
28060         
28061         if(this.rendered){
28062             
28063             for (var i =0; i < this.toolbars.length;i++) {
28064                 // fixme - ask toolbars for heights?
28065                 this.toolbars[i].onDestroy();
28066             }
28067             
28068             this.wrap.dom.innerHTML = '';
28069             this.wrap.remove();
28070         }
28071     },
28072
28073     // private
28074     onFirstFocus : function(){
28075         //Roo.log("onFirstFocus");
28076         this.editorcore.onFirstFocus();
28077          for (var i =0; i < this.toolbars.length;i++) {
28078             this.toolbars[i].onFirstFocus();
28079         }
28080         
28081     },
28082     
28083     // private
28084     syncValue : function()
28085     {   
28086         this.editorcore.syncValue();
28087     },
28088     
28089     pushValue : function()
28090     {   
28091         this.editorcore.pushValue();
28092     }
28093      
28094     
28095     // hide stuff that is not compatible
28096     /**
28097      * @event blur
28098      * @hide
28099      */
28100     /**
28101      * @event change
28102      * @hide
28103      */
28104     /**
28105      * @event focus
28106      * @hide
28107      */
28108     /**
28109      * @event specialkey
28110      * @hide
28111      */
28112     /**
28113      * @cfg {String} fieldClass @hide
28114      */
28115     /**
28116      * @cfg {String} focusClass @hide
28117      */
28118     /**
28119      * @cfg {String} autoCreate @hide
28120      */
28121     /**
28122      * @cfg {String} inputType @hide
28123      */
28124      
28125     /**
28126      * @cfg {String} invalidText @hide
28127      */
28128     /**
28129      * @cfg {String} msgFx @hide
28130      */
28131     /**
28132      * @cfg {String} validateOnBlur @hide
28133      */
28134 });
28135  
28136     
28137    
28138    
28139    
28140       
28141 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28142 /**
28143  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28144  * @parent Roo.bootstrap.form.HtmlEditor
28145  * @extends Roo.bootstrap.nav.Simplebar
28146  * Basic Toolbar
28147  * 
28148  * @example
28149  * Usage:
28150  *
28151  new Roo.bootstrap.form.HtmlEditor({
28152     ....
28153     toolbars : [
28154         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28155             disable : { fonts: 1 , format: 1, ..., ... , ...],
28156             btns : [ .... ]
28157         })
28158     }
28159      
28160  * 
28161  * @cfg {Object} disable List of elements to disable..
28162  * @cfg {Array} btns List of additional buttons.
28163  * 
28164  * 
28165  * NEEDS Extra CSS? 
28166  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28167  */
28168  
28169 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28170 {
28171     
28172     Roo.apply(this, config);
28173     
28174     // default disabled, based on 'good practice'..
28175     this.disable = this.disable || {};
28176     Roo.applyIf(this.disable, {
28177         fontSize : true,
28178         colors : true,
28179         specialElements : true
28180     });
28181     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28182     
28183     this.editor = config.editor;
28184     this.editorcore = config.editor.editorcore;
28185     
28186     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28187     
28188     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28189     // dont call parent... till later.
28190 }
28191 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
28192      
28193     bar : true,
28194     
28195     editor : false,
28196     editorcore : false,
28197     
28198     
28199     formats : [
28200         "p" ,  
28201         "h1","h2","h3","h4","h5","h6", 
28202         "pre", "code", 
28203         "abbr", "acronym", "address", "cite", "samp", "var",
28204         'div','span'
28205     ],
28206     
28207     onRender : function(ct, position)
28208     {
28209        // Roo.log("Call onRender: " + this.xtype);
28210         
28211        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28212        Roo.log(this.el);
28213        this.el.dom.style.marginBottom = '0';
28214        var _this = this;
28215        var editorcore = this.editorcore;
28216        var editor= this.editor;
28217        
28218        var children = [];
28219        var btn = function(id,cmd , toggle, handler, html){
28220        
28221             var  event = toggle ? 'toggle' : 'click';
28222        
28223             var a = {
28224                 size : 'sm',
28225                 xtype: 'Button',
28226                 xns: Roo.bootstrap,
28227                 //glyphicon : id,
28228                 fa: id,
28229                 cmd : id || cmd,
28230                 enableToggle:toggle !== false,
28231                 html : html || '',
28232                 pressed : toggle ? false : null,
28233                 listeners : {}
28234             };
28235             a.listeners[toggle ? 'toggle' : 'click'] = function() {
28236                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
28237             };
28238             children.push(a);
28239             return a;
28240        }
28241        
28242     //    var cb_box = function...
28243         
28244         var style = {
28245                 xtype: 'Button',
28246                 size : 'sm',
28247                 xns: Roo.bootstrap,
28248                 fa : 'font',
28249                 //html : 'submit'
28250                 menu : {
28251                     xtype: 'Menu',
28252                     xns: Roo.bootstrap,
28253                     items:  []
28254                 }
28255         };
28256         Roo.each(this.formats, function(f) {
28257             style.menu.items.push({
28258                 xtype :'MenuItem',
28259                 xns: Roo.bootstrap,
28260                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28261                 tagname : f,
28262                 listeners : {
28263                     click : function()
28264                     {
28265                         editorcore.insertTag(this.tagname);
28266                         editor.focus();
28267                     }
28268                 }
28269                 
28270             });
28271         });
28272         children.push(style);   
28273         
28274         btn('bold',false,true);
28275         btn('italic',false,true);
28276         btn('align-left', 'justifyleft',true);
28277         btn('align-center', 'justifycenter',true);
28278         btn('align-right' , 'justifyright',true);
28279         btn('link', false, false, function(btn) {
28280             //Roo.log("create link?");
28281             var url = prompt(this.createLinkText, this.defaultLinkValue);
28282             if(url && url != 'http:/'+'/'){
28283                 this.editorcore.relayCmd('createlink', url);
28284             }
28285         }),
28286         btn('list','insertunorderedlist',true);
28287         btn('pencil', false,true, function(btn){
28288                 Roo.log(this);
28289                 this.toggleSourceEdit(btn.pressed);
28290         });
28291         
28292         if (this.editor.btns.length > 0) {
28293             for (var i = 0; i<this.editor.btns.length; i++) {
28294                 children.push(this.editor.btns[i]);
28295             }
28296         }
28297         
28298         /*
28299         var cog = {
28300                 xtype: 'Button',
28301                 size : 'sm',
28302                 xns: Roo.bootstrap,
28303                 glyphicon : 'cog',
28304                 //html : 'submit'
28305                 menu : {
28306                     xtype: 'Menu',
28307                     xns: Roo.bootstrap,
28308                     items:  []
28309                 }
28310         };
28311         
28312         cog.menu.items.push({
28313             xtype :'MenuItem',
28314             xns: Roo.bootstrap,
28315             html : Clean styles,
28316             tagname : f,
28317             listeners : {
28318                 click : function()
28319                 {
28320                     editorcore.insertTag(this.tagname);
28321                     editor.focus();
28322                 }
28323             }
28324             
28325         });
28326        */
28327         
28328          
28329        this.xtype = 'NavSimplebar';
28330         
28331         for(var i=0;i< children.length;i++) {
28332             
28333             this.buttons.add(this.addxtypeChild(children[i]));
28334             
28335         }
28336         
28337         editor.on('editorevent', this.updateToolbar, this);
28338     },
28339     onBtnClick : function(id)
28340     {
28341        this.editorcore.relayCmd(id);
28342        this.editorcore.focus();
28343     },
28344     
28345     /**
28346      * Protected method that will not generally be called directly. It triggers
28347      * a toolbar update by reading the markup state of the current selection in the editor.
28348      */
28349     updateToolbar: function(){
28350
28351         if(!this.editorcore.activated){
28352             this.editor.onFirstFocus(); // is this neeed?
28353             return;
28354         }
28355
28356         var btns = this.buttons; 
28357         var doc = this.editorcore.doc;
28358         btns.get('bold').setActive(doc.queryCommandState('bold'));
28359         btns.get('italic').setActive(doc.queryCommandState('italic'));
28360         //btns.get('underline').setActive(doc.queryCommandState('underline'));
28361         
28362         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28363         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28364         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28365         
28366         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28367         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28368          /*
28369         
28370         var ans = this.editorcore.getAllAncestors();
28371         if (this.formatCombo) {
28372             
28373             
28374             var store = this.formatCombo.store;
28375             this.formatCombo.setValue("");
28376             for (var i =0; i < ans.length;i++) {
28377                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28378                     // select it..
28379                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28380                     break;
28381                 }
28382             }
28383         }
28384         
28385         
28386         
28387         // hides menus... - so this cant be on a menu...
28388         Roo.bootstrap.MenuMgr.hideAll();
28389         */
28390         Roo.bootstrap.menu.Manager.hideAll();
28391         //this.editorsyncValue();
28392     },
28393     onFirstFocus: function() {
28394         this.buttons.each(function(item){
28395            item.enable();
28396         });
28397     },
28398     toggleSourceEdit : function(sourceEditMode){
28399         
28400           
28401         if(sourceEditMode){
28402             Roo.log("disabling buttons");
28403            this.buttons.each( function(item){
28404                 if(item.cmd != 'pencil'){
28405                     item.disable();
28406                 }
28407             });
28408           
28409         }else{
28410             Roo.log("enabling buttons");
28411             if(this.editorcore.initialized){
28412                 this.buttons.each( function(item){
28413                     item.enable();
28414                 });
28415             }
28416             
28417         }
28418         Roo.log("calling toggole on editor");
28419         // tell the editor that it's been pressed..
28420         this.editor.toggleSourceEdit(sourceEditMode);
28421        
28422     }
28423 });
28424
28425
28426
28427
28428  
28429 /*
28430  * - LGPL
28431  */
28432
28433 /**
28434  * @class Roo.bootstrap.form.Markdown
28435  * @extends Roo.bootstrap.form.TextArea
28436  * Bootstrap Showdown editable area
28437  * @cfg {string} content
28438  * 
28439  * @constructor
28440  * Create a new Showdown
28441  */
28442
28443 Roo.bootstrap.form.Markdown = function(config){
28444     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28445    
28446 };
28447
28448 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
28449     
28450     editing :false,
28451     
28452     initEvents : function()
28453     {
28454         
28455         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28456         this.markdownEl = this.el.createChild({
28457             cls : 'roo-markdown-area'
28458         });
28459         this.inputEl().addClass('d-none');
28460         if (this.getValue() == '') {
28461             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28462             
28463         } else {
28464             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28465         }
28466         this.markdownEl.on('click', this.toggleTextEdit, this);
28467         this.on('blur', this.toggleTextEdit, this);
28468         this.on('specialkey', this.resizeTextArea, this);
28469     },
28470     
28471     toggleTextEdit : function()
28472     {
28473         var sh = this.markdownEl.getHeight();
28474         this.inputEl().addClass('d-none');
28475         this.markdownEl.addClass('d-none');
28476         if (!this.editing) {
28477             // show editor?
28478             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28479             this.inputEl().removeClass('d-none');
28480             this.inputEl().focus();
28481             this.editing = true;
28482             return;
28483         }
28484         // show showdown...
28485         this.updateMarkdown();
28486         this.markdownEl.removeClass('d-none');
28487         this.editing = false;
28488         return;
28489     },
28490     updateMarkdown : function()
28491     {
28492         if (this.getValue() == '') {
28493             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28494             return;
28495         }
28496  
28497         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28498     },
28499     
28500     resizeTextArea: function () {
28501         
28502         var sh = 100;
28503         Roo.log([sh, this.getValue().split("\n").length * 30]);
28504         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28505     },
28506     setValue : function(val)
28507     {
28508         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28509         if (!this.editing) {
28510             this.updateMarkdown();
28511         }
28512         
28513     },
28514     focus : function()
28515     {
28516         if (!this.editing) {
28517             this.toggleTextEdit();
28518         }
28519         
28520     }
28521
28522
28523 });/*
28524  * Based on:
28525  * Ext JS Library 1.1.1
28526  * Copyright(c) 2006-2007, Ext JS, LLC.
28527  *
28528  * Originally Released Under LGPL - original licence link has changed is not relivant.
28529  *
28530  * Fork - LGPL
28531  * <script type="text/javascript">
28532  */
28533  
28534 /**
28535  * @class Roo.bootstrap.PagingToolbar
28536  * @extends Roo.bootstrap.nav.Simplebar
28537  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28538  * @constructor
28539  * Create a new PagingToolbar
28540  * @param {Object} config The config object
28541  * @param {Roo.data.Store} store
28542  */
28543 Roo.bootstrap.PagingToolbar = function(config)
28544 {
28545     // old args format still supported... - xtype is prefered..
28546         // created from xtype...
28547     
28548     this.ds = config.dataSource;
28549     
28550     if (config.store && !this.ds) {
28551         this.store= Roo.factory(config.store, Roo.data);
28552         this.ds = this.store;
28553         this.ds.xmodule = this.xmodule || false;
28554     }
28555     
28556     this.toolbarItems = [];
28557     if (config.items) {
28558         this.toolbarItems = config.items;
28559     }
28560     
28561     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28562     
28563     this.cursor = 0;
28564     
28565     if (this.ds) { 
28566         this.bind(this.ds);
28567     }
28568     
28569     if (Roo.bootstrap.version == 4) {
28570         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28571     } else {
28572         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28573     }
28574     
28575 };
28576
28577 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28578     /**
28579      * @cfg {Roo.bootstrap.Button} buttons[]
28580      * Buttons for the toolbar
28581      */
28582      /**
28583      * @cfg {Roo.data.Store} store
28584      * The underlying data store providing the paged data
28585      */
28586     /**
28587      * @cfg {String/HTMLElement/Element} container
28588      * container The id or element that will contain the toolbar
28589      */
28590     /**
28591      * @cfg {Boolean} displayInfo
28592      * True to display the displayMsg (defaults to false)
28593      */
28594     /**
28595      * @cfg {Number} pageSize
28596      * The number of records to display per page (defaults to 20)
28597      */
28598     pageSize: 20,
28599     /**
28600      * @cfg {String} displayMsg
28601      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28602      */
28603     displayMsg : 'Displaying {0} - {1} of {2}',
28604     /**
28605      * @cfg {String} emptyMsg
28606      * The message to display when no records are found (defaults to "No data to display")
28607      */
28608     emptyMsg : 'No data to display',
28609     /**
28610      * Customizable piece of the default paging text (defaults to "Page")
28611      * @type String
28612      */
28613     beforePageText : "Page",
28614     /**
28615      * Customizable piece of the default paging text (defaults to "of %0")
28616      * @type String
28617      */
28618     afterPageText : "of {0}",
28619     /**
28620      * Customizable piece of the default paging text (defaults to "First Page")
28621      * @type String
28622      */
28623     firstText : "First Page",
28624     /**
28625      * Customizable piece of the default paging text (defaults to "Previous Page")
28626      * @type String
28627      */
28628     prevText : "Previous Page",
28629     /**
28630      * Customizable piece of the default paging text (defaults to "Next Page")
28631      * @type String
28632      */
28633     nextText : "Next Page",
28634     /**
28635      * Customizable piece of the default paging text (defaults to "Last Page")
28636      * @type String
28637      */
28638     lastText : "Last Page",
28639     /**
28640      * Customizable piece of the default paging text (defaults to "Refresh")
28641      * @type String
28642      */
28643     refreshText : "Refresh",
28644
28645     buttons : false,
28646     // private
28647     onRender : function(ct, position) 
28648     {
28649         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28650         this.navgroup.parentId = this.id;
28651         this.navgroup.onRender(this.el, null);
28652         // add the buttons to the navgroup
28653         
28654         if(this.displayInfo){
28655             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28656             this.displayEl = this.el.select('.x-paging-info', true).first();
28657 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28658 //            this.displayEl = navel.el.select('span',true).first();
28659         }
28660         
28661         var _this = this;
28662         
28663         if(this.buttons){
28664             Roo.each(_this.buttons, function(e){ // this might need to use render????
28665                Roo.factory(e).render(_this.el);
28666             });
28667         }
28668             
28669         Roo.each(_this.toolbarItems, function(e) {
28670             _this.navgroup.addItem(e);
28671         });
28672         
28673         
28674         this.first = this.navgroup.addItem({
28675             tooltip: this.firstText,
28676             cls: "prev btn-outline-secondary",
28677             html : ' <i class="fa fa-step-backward"></i>',
28678             disabled: true,
28679             preventDefault: true,
28680             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28681         });
28682         
28683         this.prev =  this.navgroup.addItem({
28684             tooltip: this.prevText,
28685             cls: "prev btn-outline-secondary",
28686             html : ' <i class="fa fa-backward"></i>',
28687             disabled: true,
28688             preventDefault: true,
28689             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28690         });
28691     //this.addSeparator();
28692         
28693         
28694         var field = this.navgroup.addItem( {
28695             tagtype : 'span',
28696             cls : 'x-paging-position  btn-outline-secondary',
28697              disabled: true,
28698             html : this.beforePageText  +
28699                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28700                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28701          } ); //?? escaped?
28702         
28703         this.field = field.el.select('input', true).first();
28704         this.field.on("keydown", this.onPagingKeydown, this);
28705         this.field.on("focus", function(){this.dom.select();});
28706     
28707     
28708         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28709         //this.field.setHeight(18);
28710         //this.addSeparator();
28711         this.next = this.navgroup.addItem({
28712             tooltip: this.nextText,
28713             cls: "next btn-outline-secondary",
28714             html : ' <i class="fa fa-forward"></i>',
28715             disabled: true,
28716             preventDefault: true,
28717             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28718         });
28719         this.last = this.navgroup.addItem({
28720             tooltip: this.lastText,
28721             html : ' <i class="fa fa-step-forward"></i>',
28722             cls: "next btn-outline-secondary",
28723             disabled: true,
28724             preventDefault: true,
28725             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28726         });
28727     //this.addSeparator();
28728         this.loading = this.navgroup.addItem({
28729             tooltip: this.refreshText,
28730             cls: "btn-outline-secondary",
28731             html : ' <i class="fa fa-refresh"></i>',
28732             preventDefault: true,
28733             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28734         });
28735         
28736     },
28737
28738     // private
28739     updateInfo : function(){
28740         if(this.displayEl){
28741             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28742             var msg = count == 0 ?
28743                 this.emptyMsg :
28744                 String.format(
28745                     this.displayMsg,
28746                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28747                 );
28748             this.displayEl.update(msg);
28749         }
28750     },
28751
28752     // private
28753     onLoad : function(ds, r, o)
28754     {
28755         this.cursor = o.params && o.params.start ? o.params.start : 0;
28756         
28757         var d = this.getPageData(),
28758             ap = d.activePage,
28759             ps = d.pages;
28760         
28761         
28762         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28763         this.field.dom.value = ap;
28764         this.first.setDisabled(ap == 1);
28765         this.prev.setDisabled(ap == 1);
28766         this.next.setDisabled(ap == ps);
28767         this.last.setDisabled(ap == ps);
28768         this.loading.enable();
28769         this.updateInfo();
28770     },
28771
28772     // private
28773     getPageData : function(){
28774         var total = this.ds.getTotalCount();
28775         return {
28776             total : total,
28777             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28778             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28779         };
28780     },
28781
28782     // private
28783     onLoadError : function(proxy, o){
28784         this.loading.enable();
28785         if (this.ds.events.loadexception.listeners.length  < 2) {
28786             // nothing has been assigned to loadexception except this...
28787             // so 
28788             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
28789
28790         }
28791     },
28792
28793     // private
28794     onPagingKeydown : function(e){
28795         var k = e.getKey();
28796         var d = this.getPageData();
28797         if(k == e.RETURN){
28798             var v = this.field.dom.value, pageNum;
28799             if(!v || isNaN(pageNum = parseInt(v, 10))){
28800                 this.field.dom.value = d.activePage;
28801                 return;
28802             }
28803             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28804             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28805             e.stopEvent();
28806         }
28807         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))
28808         {
28809           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28810           this.field.dom.value = pageNum;
28811           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28812           e.stopEvent();
28813         }
28814         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28815         {
28816           var v = this.field.dom.value, pageNum; 
28817           var increment = (e.shiftKey) ? 10 : 1;
28818           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28819                 increment *= -1;
28820           }
28821           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28822             this.field.dom.value = d.activePage;
28823             return;
28824           }
28825           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28826           {
28827             this.field.dom.value = parseInt(v, 10) + increment;
28828             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28829             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28830           }
28831           e.stopEvent();
28832         }
28833     },
28834
28835     // private
28836     beforeLoad : function(){
28837         if(this.loading){
28838             this.loading.disable();
28839         }
28840     },
28841
28842     // private
28843     onClick : function(which){
28844         
28845         var ds = this.ds;
28846         if (!ds) {
28847             return;
28848         }
28849         
28850         switch(which){
28851             case "first":
28852                 ds.load({params:{start: 0, limit: this.pageSize}});
28853             break;
28854             case "prev":
28855                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28856             break;
28857             case "next":
28858                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28859             break;
28860             case "last":
28861                 var total = ds.getTotalCount();
28862                 var extra = total % this.pageSize;
28863                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28864                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28865             break;
28866             case "refresh":
28867                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28868             break;
28869         }
28870     },
28871
28872     /**
28873      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28874      * @param {Roo.data.Store} store The data store to unbind
28875      */
28876     unbind : function(ds){
28877         ds.un("beforeload", this.beforeLoad, this);
28878         ds.un("load", this.onLoad, this);
28879         ds.un("loadexception", this.onLoadError, this);
28880         ds.un("remove", this.updateInfo, this);
28881         ds.un("add", this.updateInfo, this);
28882         this.ds = undefined;
28883     },
28884
28885     /**
28886      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28887      * @param {Roo.data.Store} store The data store to bind
28888      */
28889     bind : function(ds){
28890         ds.on("beforeload", this.beforeLoad, this);
28891         ds.on("load", this.onLoad, this);
28892         ds.on("loadexception", this.onLoadError, this);
28893         ds.on("remove", this.updateInfo, this);
28894         ds.on("add", this.updateInfo, this);
28895         this.ds = ds;
28896     }
28897 });/*
28898  * - LGPL
28899  *
28900  * element
28901  * 
28902  */
28903
28904 /**
28905  * @class Roo.bootstrap.MessageBar
28906  * @extends Roo.bootstrap.Component
28907  * Bootstrap MessageBar class
28908  * @cfg {String} html contents of the MessageBar
28909  * @cfg {String} weight (info | success | warning | danger) default info
28910  * @cfg {String} beforeClass insert the bar before the given class
28911  * @cfg {Boolean} closable (true | false) default false
28912  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28913  * 
28914  * @constructor
28915  * Create a new Element
28916  * @param {Object} config The config object
28917  */
28918
28919 Roo.bootstrap.MessageBar = function(config){
28920     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28921 };
28922
28923 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28924     
28925     html: '',
28926     weight: 'info',
28927     closable: false,
28928     fixed: false,
28929     beforeClass: 'bootstrap-sticky-wrap',
28930     
28931     getAutoCreate : function(){
28932         
28933         var cfg = {
28934             tag: 'div',
28935             cls: 'alert alert-dismissable alert-' + this.weight,
28936             cn: [
28937                 {
28938                     tag: 'span',
28939                     cls: 'message',
28940                     html: this.html || ''
28941                 }
28942             ]
28943         };
28944         
28945         if(this.fixed){
28946             cfg.cls += ' alert-messages-fixed';
28947         }
28948         
28949         if(this.closable){
28950             cfg.cn.push({
28951                 tag: 'button',
28952                 cls: 'close',
28953                 html: 'x'
28954             });
28955         }
28956         
28957         return cfg;
28958     },
28959     
28960     onRender : function(ct, position)
28961     {
28962         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28963         
28964         if(!this.el){
28965             var cfg = Roo.apply({},  this.getAutoCreate());
28966             cfg.id = Roo.id();
28967             
28968             if (this.cls) {
28969                 cfg.cls += ' ' + this.cls;
28970             }
28971             if (this.style) {
28972                 cfg.style = this.style;
28973             }
28974             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28975             
28976             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28977         }
28978         
28979         this.el.select('>button.close').on('click', this.hide, this);
28980         
28981     },
28982     
28983     show : function()
28984     {
28985         if (!this.rendered) {
28986             this.render();
28987         }
28988         
28989         this.el.show();
28990         
28991         this.fireEvent('show', this);
28992         
28993     },
28994     
28995     hide : function()
28996     {
28997         if (!this.rendered) {
28998             this.render();
28999         }
29000         
29001         this.el.hide();
29002         
29003         this.fireEvent('hide', this);
29004     },
29005     
29006     update : function()
29007     {
29008 //        var e = this.el.dom.firstChild;
29009 //        
29010 //        if(this.closable){
29011 //            e = e.nextSibling;
29012 //        }
29013 //        
29014 //        e.data = this.html || '';
29015
29016         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29017     }
29018    
29019 });
29020
29021  
29022
29023      /*
29024  * - LGPL
29025  *
29026  * Graph
29027  * 
29028  */
29029
29030
29031 /**
29032  * @class Roo.bootstrap.Graph
29033  * @extends Roo.bootstrap.Component
29034  * Bootstrap Graph class
29035 > Prameters
29036  -sm {number} sm 4
29037  -md {number} md 5
29038  @cfg {String} graphtype  bar | vbar | pie
29039  @cfg {number} g_x coodinator | centre x (pie)
29040  @cfg {number} g_y coodinator | centre y (pie)
29041  @cfg {number} g_r radius (pie)
29042  @cfg {number} g_height height of the chart (respected by all elements in the set)
29043  @cfg {number} g_width width of the chart (respected by all elements in the set)
29044  @cfg {Object} title The title of the chart
29045     
29046  -{Array}  values
29047  -opts (object) options for the chart 
29048      o {
29049      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29050      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29051      o vgutter (number)
29052      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.
29053      o stacked (boolean) whether or not to tread values as in a stacked bar chart
29054      o to
29055      o stretch (boolean)
29056      o }
29057  -opts (object) options for the pie
29058      o{
29059      o cut
29060      o startAngle (number)
29061      o endAngle (number)
29062      } 
29063  *
29064  * @constructor
29065  * Create a new Input
29066  * @param {Object} config The config object
29067  */
29068
29069 Roo.bootstrap.Graph = function(config){
29070     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29071     
29072     this.addEvents({
29073         // img events
29074         /**
29075          * @event click
29076          * The img click event for the img.
29077          * @param {Roo.EventObject} e
29078          */
29079         "click" : true
29080     });
29081 };
29082
29083 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
29084     
29085     sm: 4,
29086     md: 5,
29087     graphtype: 'bar',
29088     g_height: 250,
29089     g_width: 400,
29090     g_x: 50,
29091     g_y: 50,
29092     g_r: 30,
29093     opts:{
29094         //g_colors: this.colors,
29095         g_type: 'soft',
29096         g_gutter: '20%'
29097
29098     },
29099     title : false,
29100
29101     getAutoCreate : function(){
29102         
29103         var cfg = {
29104             tag: 'div',
29105             html : null
29106         };
29107         
29108         
29109         return  cfg;
29110     },
29111
29112     onRender : function(ct,position){
29113         
29114         
29115         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29116         
29117         if (typeof(Raphael) == 'undefined') {
29118             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29119             return;
29120         }
29121         
29122         this.raphael = Raphael(this.el.dom);
29123         
29124                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29125                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29126                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29127                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29128                 /*
29129                 r.text(160, 10, "Single Series Chart").attr(txtattr);
29130                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29131                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29132                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29133                 
29134                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29135                 r.barchart(330, 10, 300, 220, data1);
29136                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29137                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29138                 */
29139                 
29140                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29141                 // r.barchart(30, 30, 560, 250,  xdata, {
29142                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29143                 //     axis : "0 0 1 1",
29144                 //     axisxlabels :  xdata
29145                 //     //yvalues : cols,
29146                    
29147                 // });
29148 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29149 //        
29150 //        this.load(null,xdata,{
29151 //                axis : "0 0 1 1",
29152 //                axisxlabels :  xdata
29153 //                });
29154
29155     },
29156
29157     load : function(graphtype,xdata,opts)
29158     {
29159         this.raphael.clear();
29160         if(!graphtype) {
29161             graphtype = this.graphtype;
29162         }
29163         if(!opts){
29164             opts = this.opts;
29165         }
29166         var r = this.raphael,
29167             fin = function () {
29168                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29169             },
29170             fout = function () {
29171                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29172             },
29173             pfin = function() {
29174                 this.sector.stop();
29175                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29176
29177                 if (this.label) {
29178                     this.label[0].stop();
29179                     this.label[0].attr({ r: 7.5 });
29180                     this.label[1].attr({ "font-weight": 800 });
29181                 }
29182             },
29183             pfout = function() {
29184                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29185
29186                 if (this.label) {
29187                     this.label[0].animate({ r: 5 }, 500, "bounce");
29188                     this.label[1].attr({ "font-weight": 400 });
29189                 }
29190             };
29191
29192         switch(graphtype){
29193             case 'bar':
29194                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29195                 break;
29196             case 'hbar':
29197                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29198                 break;
29199             case 'pie':
29200 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
29201 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29202 //            
29203                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29204                 
29205                 break;
29206
29207         }
29208         
29209         if(this.title){
29210             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29211         }
29212         
29213     },
29214     
29215     setTitle: function(o)
29216     {
29217         this.title = o;
29218     },
29219     
29220     initEvents: function() {
29221         
29222         if(!this.href){
29223             this.el.on('click', this.onClick, this);
29224         }
29225     },
29226     
29227     onClick : function(e)
29228     {
29229         Roo.log('img onclick');
29230         this.fireEvent('click', this, e);
29231     }
29232    
29233 });
29234
29235  
29236 /*
29237  * - LGPL
29238  *
29239  * numberBox
29240  * 
29241  */
29242 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29243
29244 /**
29245  * @class Roo.bootstrap.dash.NumberBox
29246  * @extends Roo.bootstrap.Component
29247  * Bootstrap NumberBox class
29248  * @cfg {String} headline Box headline
29249  * @cfg {String} content Box content
29250  * @cfg {String} icon Box icon
29251  * @cfg {String} footer Footer text
29252  * @cfg {String} fhref Footer href
29253  * 
29254  * @constructor
29255  * Create a new NumberBox
29256  * @param {Object} config The config object
29257  */
29258
29259
29260 Roo.bootstrap.dash.NumberBox = function(config){
29261     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29262     
29263 };
29264
29265 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
29266     
29267     headline : '',
29268     content : '',
29269     icon : '',
29270     footer : '',
29271     fhref : '',
29272     ficon : '',
29273     
29274     getAutoCreate : function(){
29275         
29276         var cfg = {
29277             tag : 'div',
29278             cls : 'small-box ',
29279             cn : [
29280                 {
29281                     tag : 'div',
29282                     cls : 'inner',
29283                     cn :[
29284                         {
29285                             tag : 'h3',
29286                             cls : 'roo-headline',
29287                             html : this.headline
29288                         },
29289                         {
29290                             tag : 'p',
29291                             cls : 'roo-content',
29292                             html : this.content
29293                         }
29294                     ]
29295                 }
29296             ]
29297         };
29298         
29299         if(this.icon){
29300             cfg.cn.push({
29301                 tag : 'div',
29302                 cls : 'icon',
29303                 cn :[
29304                     {
29305                         tag : 'i',
29306                         cls : 'ion ' + this.icon
29307                     }
29308                 ]
29309             });
29310         }
29311         
29312         if(this.footer){
29313             var footer = {
29314                 tag : 'a',
29315                 cls : 'small-box-footer',
29316                 href : this.fhref || '#',
29317                 html : this.footer
29318             };
29319             
29320             cfg.cn.push(footer);
29321             
29322         }
29323         
29324         return  cfg;
29325     },
29326
29327     onRender : function(ct,position){
29328         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29329
29330
29331        
29332                 
29333     },
29334
29335     setHeadline: function (value)
29336     {
29337         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29338     },
29339     
29340     setFooter: function (value, href)
29341     {
29342         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29343         
29344         if(href){
29345             this.el.select('a.small-box-footer',true).first().attr('href', href);
29346         }
29347         
29348     },
29349
29350     setContent: function (value)
29351     {
29352         this.el.select('.roo-content',true).first().dom.innerHTML = value;
29353     },
29354
29355     initEvents: function() 
29356     {   
29357         
29358     }
29359     
29360 });
29361
29362  
29363 /*
29364  * - LGPL
29365  *
29366  * TabBox
29367  * 
29368  */
29369 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29370
29371 /**
29372  * @class Roo.bootstrap.dash.TabBox
29373  * @extends Roo.bootstrap.Component
29374  * @children Roo.bootstrap.dash.TabPane
29375  * Bootstrap TabBox class
29376  * @cfg {String} title Title of the TabBox
29377  * @cfg {String} icon Icon of the TabBox
29378  * @cfg {Boolean} showtabs (true|false) show the tabs default true
29379  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29380  * 
29381  * @constructor
29382  * Create a new TabBox
29383  * @param {Object} config The config object
29384  */
29385
29386
29387 Roo.bootstrap.dash.TabBox = function(config){
29388     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29389     this.addEvents({
29390         // raw events
29391         /**
29392          * @event addpane
29393          * When a pane is added
29394          * @param {Roo.bootstrap.dash.TabPane} pane
29395          */
29396         "addpane" : true,
29397         /**
29398          * @event activatepane
29399          * When a pane is activated
29400          * @param {Roo.bootstrap.dash.TabPane} pane
29401          */
29402         "activatepane" : true
29403         
29404          
29405     });
29406     
29407     this.panes = [];
29408 };
29409
29410 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
29411
29412     title : '',
29413     icon : false,
29414     showtabs : true,
29415     tabScrollable : false,
29416     
29417     getChildContainer : function()
29418     {
29419         return this.el.select('.tab-content', true).first();
29420     },
29421     
29422     getAutoCreate : function(){
29423         
29424         var header = {
29425             tag: 'li',
29426             cls: 'pull-left header',
29427             html: this.title,
29428             cn : []
29429         };
29430         
29431         if(this.icon){
29432             header.cn.push({
29433                 tag: 'i',
29434                 cls: 'fa ' + this.icon
29435             });
29436         }
29437         
29438         var h = {
29439             tag: 'ul',
29440             cls: 'nav nav-tabs pull-right',
29441             cn: [
29442                 header
29443             ]
29444         };
29445         
29446         if(this.tabScrollable){
29447             h = {
29448                 tag: 'div',
29449                 cls: 'tab-header',
29450                 cn: [
29451                     {
29452                         tag: 'ul',
29453                         cls: 'nav nav-tabs pull-right',
29454                         cn: [
29455                             header
29456                         ]
29457                     }
29458                 ]
29459             };
29460         }
29461         
29462         var cfg = {
29463             tag: 'div',
29464             cls: 'nav-tabs-custom',
29465             cn: [
29466                 h,
29467                 {
29468                     tag: 'div',
29469                     cls: 'tab-content no-padding',
29470                     cn: []
29471                 }
29472             ]
29473         };
29474
29475         return  cfg;
29476     },
29477     initEvents : function()
29478     {
29479         //Roo.log('add add pane handler');
29480         this.on('addpane', this.onAddPane, this);
29481     },
29482      /**
29483      * Updates the box title
29484      * @param {String} html to set the title to.
29485      */
29486     setTitle : function(value)
29487     {
29488         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29489     },
29490     onAddPane : function(pane)
29491     {
29492         this.panes.push(pane);
29493         //Roo.log('addpane');
29494         //Roo.log(pane);
29495         // tabs are rendere left to right..
29496         if(!this.showtabs){
29497             return;
29498         }
29499         
29500         var ctr = this.el.select('.nav-tabs', true).first();
29501          
29502          
29503         var existing = ctr.select('.nav-tab',true);
29504         var qty = existing.getCount();;
29505         
29506         
29507         var tab = ctr.createChild({
29508             tag : 'li',
29509             cls : 'nav-tab' + (qty ? '' : ' active'),
29510             cn : [
29511                 {
29512                     tag : 'a',
29513                     href:'#',
29514                     html : pane.title
29515                 }
29516             ]
29517         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29518         pane.tab = tab;
29519         
29520         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29521         if (!qty) {
29522             pane.el.addClass('active');
29523         }
29524         
29525                 
29526     },
29527     onTabClick : function(ev,un,ob,pane)
29528     {
29529         //Roo.log('tab - prev default');
29530         ev.preventDefault();
29531         
29532         
29533         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29534         pane.tab.addClass('active');
29535         //Roo.log(pane.title);
29536         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29537         // technically we should have a deactivate event.. but maybe add later.
29538         // and it should not de-activate the selected tab...
29539         this.fireEvent('activatepane', pane);
29540         pane.el.addClass('active');
29541         pane.fireEvent('activate');
29542         
29543         
29544     },
29545     
29546     getActivePane : function()
29547     {
29548         var r = false;
29549         Roo.each(this.panes, function(p) {
29550             if(p.el.hasClass('active')){
29551                 r = p;
29552                 return false;
29553             }
29554             
29555             return;
29556         });
29557         
29558         return r;
29559     }
29560     
29561     
29562 });
29563
29564  
29565 /*
29566  * - LGPL
29567  *
29568  * Tab pane
29569  * 
29570  */
29571 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29572 /**
29573  * @class Roo.bootstrap.TabPane
29574  * @extends Roo.bootstrap.Component
29575  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
29576  * Bootstrap TabPane class
29577  * @cfg {Boolean} active (false | true) Default false
29578  * @cfg {String} title title of panel
29579
29580  * 
29581  * @constructor
29582  * Create a new TabPane
29583  * @param {Object} config The config object
29584  */
29585
29586 Roo.bootstrap.dash.TabPane = function(config){
29587     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29588     
29589     this.addEvents({
29590         // raw events
29591         /**
29592          * @event activate
29593          * When a pane is activated
29594          * @param {Roo.bootstrap.dash.TabPane} pane
29595          */
29596         "activate" : true
29597          
29598     });
29599 };
29600
29601 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29602     
29603     active : false,
29604     title : '',
29605     
29606     // the tabBox that this is attached to.
29607     tab : false,
29608      
29609     getAutoCreate : function() 
29610     {
29611         var cfg = {
29612             tag: 'div',
29613             cls: 'tab-pane'
29614         };
29615         
29616         if(this.active){
29617             cfg.cls += ' active';
29618         }
29619         
29620         return cfg;
29621     },
29622     initEvents  : function()
29623     {
29624         //Roo.log('trigger add pane handler');
29625         this.parent().fireEvent('addpane', this)
29626     },
29627     
29628      /**
29629      * Updates the tab title 
29630      * @param {String} html to set the title to.
29631      */
29632     setTitle: function(str)
29633     {
29634         if (!this.tab) {
29635             return;
29636         }
29637         this.title = str;
29638         this.tab.select('a', true).first().dom.innerHTML = str;
29639         
29640     }
29641     
29642     
29643     
29644 });
29645
29646  
29647
29648
29649  /*
29650  * - LGPL
29651  *
29652  * Tooltip
29653  * 
29654  */
29655
29656 /**
29657  * @class Roo.bootstrap.Tooltip
29658  * Bootstrap Tooltip class
29659  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29660  * to determine which dom element triggers the tooltip.
29661  * 
29662  * It needs to add support for additional attributes like tooltip-position
29663  * 
29664  * @constructor
29665  * Create a new Toolti
29666  * @param {Object} config The config object
29667  */
29668
29669 Roo.bootstrap.Tooltip = function(config){
29670     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29671     
29672     this.alignment = Roo.bootstrap.Tooltip.alignment;
29673     
29674     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29675         this.alignment = config.alignment;
29676     }
29677     
29678 };
29679
29680 Roo.apply(Roo.bootstrap.Tooltip, {
29681     /**
29682      * @function init initialize tooltip monitoring.
29683      * @static
29684      */
29685     currentEl : false,
29686     currentTip : false,
29687     currentRegion : false,
29688     
29689     //  init : delay?
29690     
29691     init : function()
29692     {
29693         Roo.get(document).on('mouseover', this.enter ,this);
29694         Roo.get(document).on('mouseout', this.leave, this);
29695          
29696         
29697         this.currentTip = new Roo.bootstrap.Tooltip();
29698     },
29699     
29700     enter : function(ev)
29701     {
29702         var dom = ev.getTarget();
29703         
29704         //Roo.log(['enter',dom]);
29705         var el = Roo.fly(dom);
29706         if (this.currentEl) {
29707             //Roo.log(dom);
29708             //Roo.log(this.currentEl);
29709             //Roo.log(this.currentEl.contains(dom));
29710             if (this.currentEl == el) {
29711                 return;
29712             }
29713             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29714                 return;
29715             }
29716
29717         }
29718         
29719         if (this.currentTip.el) {
29720             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29721         }    
29722         //Roo.log(ev);
29723         
29724         if(!el || el.dom == document){
29725             return;
29726         }
29727         
29728         var bindEl = el; 
29729         var pel = false;
29730         if (!el.attr('tooltip')) {
29731             pel = el.findParent("[tooltip]");
29732             if (pel) {
29733                 bindEl = Roo.get(pel);
29734             }
29735         }
29736         
29737        
29738         
29739         // you can not look for children, as if el is the body.. then everythign is the child..
29740         if (!pel && !el.attr('tooltip')) { //
29741             if (!el.select("[tooltip]").elements.length) {
29742                 return;
29743             }
29744             // is the mouse over this child...?
29745             bindEl = el.select("[tooltip]").first();
29746             var xy = ev.getXY();
29747             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29748                 //Roo.log("not in region.");
29749                 return;
29750             }
29751             //Roo.log("child element over..");
29752             
29753         }
29754         this.currentEl = el;
29755         this.currentTip.bind(bindEl);
29756         this.currentRegion = Roo.lib.Region.getRegion(dom);
29757         this.currentTip.enter();
29758         
29759     },
29760     leave : function(ev)
29761     {
29762         var dom = ev.getTarget();
29763         //Roo.log(['leave',dom]);
29764         if (!this.currentEl) {
29765             return;
29766         }
29767         
29768         
29769         if (dom != this.currentEl.dom) {
29770             return;
29771         }
29772         var xy = ev.getXY();
29773         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29774             return;
29775         }
29776         // only activate leave if mouse cursor is outside... bounding box..
29777         
29778         
29779         
29780         
29781         if (this.currentTip) {
29782             this.currentTip.leave();
29783         }
29784         //Roo.log('clear currentEl');
29785         this.currentEl = false;
29786         
29787         
29788     },
29789     alignment : {
29790         'left' : ['r-l', [-2,0], 'right'],
29791         'right' : ['l-r', [2,0], 'left'],
29792         'bottom' : ['t-b', [0,2], 'top'],
29793         'top' : [ 'b-t', [0,-2], 'bottom']
29794     }
29795     
29796 });
29797
29798
29799 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29800     
29801     
29802     bindEl : false,
29803     
29804     delay : null, // can be { show : 300 , hide: 500}
29805     
29806     timeout : null,
29807     
29808     hoverState : null, //???
29809     
29810     placement : 'bottom', 
29811     
29812     alignment : false,
29813     
29814     getAutoCreate : function(){
29815     
29816         var cfg = {
29817            cls : 'tooltip',   
29818            role : 'tooltip',
29819            cn : [
29820                 {
29821                     cls : 'tooltip-arrow arrow'
29822                 },
29823                 {
29824                     cls : 'tooltip-inner'
29825                 }
29826            ]
29827         };
29828         
29829         return cfg;
29830     },
29831     bind : function(el)
29832     {
29833         this.bindEl = el;
29834     },
29835     
29836     initEvents : function()
29837     {
29838         this.arrowEl = this.el.select('.arrow', true).first();
29839         this.innerEl = this.el.select('.tooltip-inner', true).first();
29840     },
29841     
29842     enter : function () {
29843        
29844         if (this.timeout != null) {
29845             clearTimeout(this.timeout);
29846         }
29847         
29848         this.hoverState = 'in';
29849          //Roo.log("enter - show");
29850         if (!this.delay || !this.delay.show) {
29851             this.show();
29852             return;
29853         }
29854         var _t = this;
29855         this.timeout = setTimeout(function () {
29856             if (_t.hoverState == 'in') {
29857                 _t.show();
29858             }
29859         }, this.delay.show);
29860     },
29861     leave : function()
29862     {
29863         clearTimeout(this.timeout);
29864     
29865         this.hoverState = 'out';
29866          if (!this.delay || !this.delay.hide) {
29867             this.hide();
29868             return;
29869         }
29870        
29871         var _t = this;
29872         this.timeout = setTimeout(function () {
29873             //Roo.log("leave - timeout");
29874             
29875             if (_t.hoverState == 'out') {
29876                 _t.hide();
29877                 Roo.bootstrap.Tooltip.currentEl = false;
29878             }
29879         }, delay);
29880     },
29881     
29882     show : function (msg)
29883     {
29884         if (!this.el) {
29885             this.render(document.body);
29886         }
29887         // set content.
29888         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29889         
29890         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29891         
29892         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29893         
29894         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29895                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29896         
29897         var placement = typeof this.placement == 'function' ?
29898             this.placement.call(this, this.el, on_el) :
29899             this.placement;
29900             
29901         var autoToken = /\s?auto?\s?/i;
29902         var autoPlace = autoToken.test(placement);
29903         if (autoPlace) {
29904             placement = placement.replace(autoToken, '') || 'top';
29905         }
29906         
29907         //this.el.detach()
29908         //this.el.setXY([0,0]);
29909         this.el.show();
29910         //this.el.dom.style.display='block';
29911         
29912         //this.el.appendTo(on_el);
29913         
29914         var p = this.getPosition();
29915         var box = this.el.getBox();
29916         
29917         if (autoPlace) {
29918             // fixme..
29919         }
29920         
29921         var align = this.alignment[placement];
29922         
29923         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29924         
29925         if(placement == 'top' || placement == 'bottom'){
29926             if(xy[0] < 0){
29927                 placement = 'right';
29928             }
29929             
29930             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29931                 placement = 'left';
29932             }
29933             
29934             var scroll = Roo.select('body', true).first().getScroll();
29935             
29936             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29937                 placement = 'top';
29938             }
29939             
29940             align = this.alignment[placement];
29941             
29942             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29943             
29944         }
29945         
29946         var elems = document.getElementsByTagName('div');
29947         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29948         for (var i = 0; i < elems.length; i++) {
29949           var zindex = Number.parseInt(
29950                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29951                 10
29952           );
29953           if (zindex > highest) {
29954             highest = zindex;
29955           }
29956         }
29957         
29958         
29959         
29960         this.el.dom.style.zIndex = highest;
29961         
29962         this.el.alignTo(this.bindEl, align[0],align[1]);
29963         //var arrow = this.el.select('.arrow',true).first();
29964         //arrow.set(align[2], 
29965         
29966         this.el.addClass(placement);
29967         this.el.addClass("bs-tooltip-"+ placement);
29968         
29969         this.el.addClass('in fade show');
29970         
29971         this.hoverState = null;
29972         
29973         if (this.el.hasClass('fade')) {
29974             // fade it?
29975         }
29976         
29977         
29978         
29979         
29980         
29981     },
29982     hide : function()
29983     {
29984          
29985         if (!this.el) {
29986             return;
29987         }
29988         //this.el.setXY([0,0]);
29989         this.el.removeClass(['show', 'in']);
29990         //this.el.hide();
29991         
29992     }
29993     
29994 });
29995  
29996
29997  /*
29998  * - LGPL
29999  *
30000  * Location Picker
30001  * 
30002  */
30003
30004 /**
30005  * @class Roo.bootstrap.LocationPicker
30006  * @extends Roo.bootstrap.Component
30007  * Bootstrap LocationPicker class
30008  * @cfg {Number} latitude Position when init default 0
30009  * @cfg {Number} longitude Position when init default 0
30010  * @cfg {Number} zoom default 15
30011  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30012  * @cfg {Boolean} mapTypeControl default false
30013  * @cfg {Boolean} disableDoubleClickZoom default false
30014  * @cfg {Boolean} scrollwheel default true
30015  * @cfg {Boolean} streetViewControl default false
30016  * @cfg {Number} radius default 0
30017  * @cfg {String} locationName
30018  * @cfg {Boolean} draggable default true
30019  * @cfg {Boolean} enableAutocomplete default false
30020  * @cfg {Boolean} enableReverseGeocode default true
30021  * @cfg {String} markerTitle
30022  * 
30023  * @constructor
30024  * Create a new LocationPicker
30025  * @param {Object} config The config object
30026  */
30027
30028
30029 Roo.bootstrap.LocationPicker = function(config){
30030     
30031     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30032     
30033     this.addEvents({
30034         /**
30035          * @event initial
30036          * Fires when the picker initialized.
30037          * @param {Roo.bootstrap.LocationPicker} this
30038          * @param {Google Location} location
30039          */
30040         initial : true,
30041         /**
30042          * @event positionchanged
30043          * Fires when the picker position changed.
30044          * @param {Roo.bootstrap.LocationPicker} this
30045          * @param {Google Location} location
30046          */
30047         positionchanged : true,
30048         /**
30049          * @event resize
30050          * Fires when the map resize.
30051          * @param {Roo.bootstrap.LocationPicker} this
30052          */
30053         resize : true,
30054         /**
30055          * @event show
30056          * Fires when the map show.
30057          * @param {Roo.bootstrap.LocationPicker} this
30058          */
30059         show : true,
30060         /**
30061          * @event hide
30062          * Fires when the map hide.
30063          * @param {Roo.bootstrap.LocationPicker} this
30064          */
30065         hide : true,
30066         /**
30067          * @event mapClick
30068          * Fires when click the map.
30069          * @param {Roo.bootstrap.LocationPicker} this
30070          * @param {Map event} e
30071          */
30072         mapClick : true,
30073         /**
30074          * @event mapRightClick
30075          * Fires when right click the map.
30076          * @param {Roo.bootstrap.LocationPicker} this
30077          * @param {Map event} e
30078          */
30079         mapRightClick : true,
30080         /**
30081          * @event markerClick
30082          * Fires when click the marker.
30083          * @param {Roo.bootstrap.LocationPicker} this
30084          * @param {Map event} e
30085          */
30086         markerClick : true,
30087         /**
30088          * @event markerRightClick
30089          * Fires when right click the marker.
30090          * @param {Roo.bootstrap.LocationPicker} this
30091          * @param {Map event} e
30092          */
30093         markerRightClick : true,
30094         /**
30095          * @event OverlayViewDraw
30096          * Fires when OverlayView Draw
30097          * @param {Roo.bootstrap.LocationPicker} this
30098          */
30099         OverlayViewDraw : true,
30100         /**
30101          * @event OverlayViewOnAdd
30102          * Fires when OverlayView Draw
30103          * @param {Roo.bootstrap.LocationPicker} this
30104          */
30105         OverlayViewOnAdd : true,
30106         /**
30107          * @event OverlayViewOnRemove
30108          * Fires when OverlayView Draw
30109          * @param {Roo.bootstrap.LocationPicker} this
30110          */
30111         OverlayViewOnRemove : true,
30112         /**
30113          * @event OverlayViewShow
30114          * Fires when OverlayView Draw
30115          * @param {Roo.bootstrap.LocationPicker} this
30116          * @param {Pixel} cpx
30117          */
30118         OverlayViewShow : true,
30119         /**
30120          * @event OverlayViewHide
30121          * Fires when OverlayView Draw
30122          * @param {Roo.bootstrap.LocationPicker} this
30123          */
30124         OverlayViewHide : true,
30125         /**
30126          * @event loadexception
30127          * Fires when load google lib failed.
30128          * @param {Roo.bootstrap.LocationPicker} this
30129          */
30130         loadexception : true
30131     });
30132         
30133 };
30134
30135 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30136     
30137     gMapContext: false,
30138     
30139     latitude: 0,
30140     longitude: 0,
30141     zoom: 15,
30142     mapTypeId: false,
30143     mapTypeControl: false,
30144     disableDoubleClickZoom: false,
30145     scrollwheel: true,
30146     streetViewControl: false,
30147     radius: 0,
30148     locationName: '',
30149     draggable: true,
30150     enableAutocomplete: false,
30151     enableReverseGeocode: true,
30152     markerTitle: '',
30153     
30154     getAutoCreate: function()
30155     {
30156
30157         var cfg = {
30158             tag: 'div',
30159             cls: 'roo-location-picker'
30160         };
30161         
30162         return cfg
30163     },
30164     
30165     initEvents: function(ct, position)
30166     {       
30167         if(!this.el.getWidth() || this.isApplied()){
30168             return;
30169         }
30170         
30171         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30172         
30173         this.initial();
30174     },
30175     
30176     initial: function()
30177     {
30178         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30179             this.fireEvent('loadexception', this);
30180             return;
30181         }
30182         
30183         if(!this.mapTypeId){
30184             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30185         }
30186         
30187         this.gMapContext = this.GMapContext();
30188         
30189         this.initOverlayView();
30190         
30191         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30192         
30193         var _this = this;
30194                 
30195         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30196             _this.setPosition(_this.gMapContext.marker.position);
30197         });
30198         
30199         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30200             _this.fireEvent('mapClick', this, event);
30201             
30202         });
30203
30204         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30205             _this.fireEvent('mapRightClick', this, event);
30206             
30207         });
30208         
30209         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30210             _this.fireEvent('markerClick', this, event);
30211             
30212         });
30213
30214         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30215             _this.fireEvent('markerRightClick', this, event);
30216             
30217         });
30218         
30219         this.setPosition(this.gMapContext.location);
30220         
30221         this.fireEvent('initial', this, this.gMapContext.location);
30222     },
30223     
30224     initOverlayView: function()
30225     {
30226         var _this = this;
30227         
30228         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30229             
30230             draw: function()
30231             {
30232                 _this.fireEvent('OverlayViewDraw', _this);
30233             },
30234             
30235             onAdd: function()
30236             {
30237                 _this.fireEvent('OverlayViewOnAdd', _this);
30238             },
30239             
30240             onRemove: function()
30241             {
30242                 _this.fireEvent('OverlayViewOnRemove', _this);
30243             },
30244             
30245             show: function(cpx)
30246             {
30247                 _this.fireEvent('OverlayViewShow', _this, cpx);
30248             },
30249             
30250             hide: function()
30251             {
30252                 _this.fireEvent('OverlayViewHide', _this);
30253             }
30254             
30255         });
30256     },
30257     
30258     fromLatLngToContainerPixel: function(event)
30259     {
30260         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30261     },
30262     
30263     isApplied: function() 
30264     {
30265         return this.getGmapContext() == false ? false : true;
30266     },
30267     
30268     getGmapContext: function() 
30269     {
30270         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30271     },
30272     
30273     GMapContext: function() 
30274     {
30275         var position = new google.maps.LatLng(this.latitude, this.longitude);
30276         
30277         var _map = new google.maps.Map(this.el.dom, {
30278             center: position,
30279             zoom: this.zoom,
30280             mapTypeId: this.mapTypeId,
30281             mapTypeControl: this.mapTypeControl,
30282             disableDoubleClickZoom: this.disableDoubleClickZoom,
30283             scrollwheel: this.scrollwheel,
30284             streetViewControl: this.streetViewControl,
30285             locationName: this.locationName,
30286             draggable: this.draggable,
30287             enableAutocomplete: this.enableAutocomplete,
30288             enableReverseGeocode: this.enableReverseGeocode
30289         });
30290         
30291         var _marker = new google.maps.Marker({
30292             position: position,
30293             map: _map,
30294             title: this.markerTitle,
30295             draggable: this.draggable
30296         });
30297         
30298         return {
30299             map: _map,
30300             marker: _marker,
30301             circle: null,
30302             location: position,
30303             radius: this.radius,
30304             locationName: this.locationName,
30305             addressComponents: {
30306                 formatted_address: null,
30307                 addressLine1: null,
30308                 addressLine2: null,
30309                 streetName: null,
30310                 streetNumber: null,
30311                 city: null,
30312                 district: null,
30313                 state: null,
30314                 stateOrProvince: null
30315             },
30316             settings: this,
30317             domContainer: this.el.dom,
30318             geodecoder: new google.maps.Geocoder()
30319         };
30320     },
30321     
30322     drawCircle: function(center, radius, options) 
30323     {
30324         if (this.gMapContext.circle != null) {
30325             this.gMapContext.circle.setMap(null);
30326         }
30327         if (radius > 0) {
30328             radius *= 1;
30329             options = Roo.apply({}, options, {
30330                 strokeColor: "#0000FF",
30331                 strokeOpacity: .35,
30332                 strokeWeight: 2,
30333                 fillColor: "#0000FF",
30334                 fillOpacity: .2
30335             });
30336             
30337             options.map = this.gMapContext.map;
30338             options.radius = radius;
30339             options.center = center;
30340             this.gMapContext.circle = new google.maps.Circle(options);
30341             return this.gMapContext.circle;
30342         }
30343         
30344         return null;
30345     },
30346     
30347     setPosition: function(location) 
30348     {
30349         this.gMapContext.location = location;
30350         this.gMapContext.marker.setPosition(location);
30351         this.gMapContext.map.panTo(location);
30352         this.drawCircle(location, this.gMapContext.radius, {});
30353         
30354         var _this = this;
30355         
30356         if (this.gMapContext.settings.enableReverseGeocode) {
30357             this.gMapContext.geodecoder.geocode({
30358                 latLng: this.gMapContext.location
30359             }, function(results, status) {
30360                 
30361                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30362                     _this.gMapContext.locationName = results[0].formatted_address;
30363                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30364                     
30365                     _this.fireEvent('positionchanged', this, location);
30366                 }
30367             });
30368             
30369             return;
30370         }
30371         
30372         this.fireEvent('positionchanged', this, location);
30373     },
30374     
30375     resize: function()
30376     {
30377         google.maps.event.trigger(this.gMapContext.map, "resize");
30378         
30379         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30380         
30381         this.fireEvent('resize', this);
30382     },
30383     
30384     setPositionByLatLng: function(latitude, longitude)
30385     {
30386         this.setPosition(new google.maps.LatLng(latitude, longitude));
30387     },
30388     
30389     getCurrentPosition: function() 
30390     {
30391         return {
30392             latitude: this.gMapContext.location.lat(),
30393             longitude: this.gMapContext.location.lng()
30394         };
30395     },
30396     
30397     getAddressName: function() 
30398     {
30399         return this.gMapContext.locationName;
30400     },
30401     
30402     getAddressComponents: function() 
30403     {
30404         return this.gMapContext.addressComponents;
30405     },
30406     
30407     address_component_from_google_geocode: function(address_components) 
30408     {
30409         var result = {};
30410         
30411         for (var i = 0; i < address_components.length; i++) {
30412             var component = address_components[i];
30413             if (component.types.indexOf("postal_code") >= 0) {
30414                 result.postalCode = component.short_name;
30415             } else if (component.types.indexOf("street_number") >= 0) {
30416                 result.streetNumber = component.short_name;
30417             } else if (component.types.indexOf("route") >= 0) {
30418                 result.streetName = component.short_name;
30419             } else if (component.types.indexOf("neighborhood") >= 0) {
30420                 result.city = component.short_name;
30421             } else if (component.types.indexOf("locality") >= 0) {
30422                 result.city = component.short_name;
30423             } else if (component.types.indexOf("sublocality") >= 0) {
30424                 result.district = component.short_name;
30425             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30426                 result.stateOrProvince = component.short_name;
30427             } else if (component.types.indexOf("country") >= 0) {
30428                 result.country = component.short_name;
30429             }
30430         }
30431         
30432         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30433         result.addressLine2 = "";
30434         return result;
30435     },
30436     
30437     setZoomLevel: function(zoom)
30438     {
30439         this.gMapContext.map.setZoom(zoom);
30440     },
30441     
30442     show: function()
30443     {
30444         if(!this.el){
30445             return;
30446         }
30447         
30448         this.el.show();
30449         
30450         this.resize();
30451         
30452         this.fireEvent('show', this);
30453     },
30454     
30455     hide: function()
30456     {
30457         if(!this.el){
30458             return;
30459         }
30460         
30461         this.el.hide();
30462         
30463         this.fireEvent('hide', this);
30464     }
30465     
30466 });
30467
30468 Roo.apply(Roo.bootstrap.LocationPicker, {
30469     
30470     OverlayView : function(map, options)
30471     {
30472         options = options || {};
30473         
30474         this.setMap(map);
30475     }
30476     
30477     
30478 });/**
30479  * @class Roo.bootstrap.Alert
30480  * @extends Roo.bootstrap.Component
30481  * Bootstrap Alert class - shows an alert area box
30482  * eg
30483  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30484   Enter a valid email address
30485 </div>
30486  * @licence LGPL
30487  * @cfg {String} title The title of alert
30488  * @cfg {String} html The content of alert
30489  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30490  * @cfg {String} fa font-awesomeicon
30491  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30492  * @cfg {Boolean} close true to show a x closer
30493  * 
30494  * 
30495  * @constructor
30496  * Create a new alert
30497  * @param {Object} config The config object
30498  */
30499
30500
30501 Roo.bootstrap.Alert = function(config){
30502     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30503     
30504 };
30505
30506 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30507     
30508     title: '',
30509     html: '',
30510     weight: false,
30511     fa: false,
30512     faicon: false, // BC
30513     close : false,
30514     
30515     
30516     getAutoCreate : function()
30517     {
30518         
30519         var cfg = {
30520             tag : 'div',
30521             cls : 'alert',
30522             cn : [
30523                 {
30524                     tag: 'button',
30525                     type :  "button",
30526                     cls: "close",
30527                     html : '×',
30528                     style : this.close ? '' : 'display:none'
30529                 },
30530                 {
30531                     tag : 'i',
30532                     cls : 'roo-alert-icon'
30533                     
30534                 },
30535                 {
30536                     tag : 'b',
30537                     cls : 'roo-alert-title',
30538                     html : this.title
30539                 },
30540                 {
30541                     tag : 'span',
30542                     cls : 'roo-alert-text',
30543                     html : this.html
30544                 }
30545             ]
30546         };
30547         
30548         if(this.faicon){
30549             cfg.cn[0].cls += ' fa ' + this.faicon;
30550         }
30551         if(this.fa){
30552             cfg.cn[0].cls += ' fa ' + this.fa;
30553         }
30554         
30555         if(this.weight){
30556             cfg.cls += ' alert-' + this.weight;
30557         }
30558         
30559         return cfg;
30560     },
30561     
30562     initEvents: function() 
30563     {
30564         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30565         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30566         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30567         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30568         if (this.seconds > 0) {
30569             this.hide.defer(this.seconds, this);
30570         }
30571     },
30572     /**
30573      * Set the Title Message HTML
30574      * @param {String} html
30575      */
30576     setTitle : function(str)
30577     {
30578         this.titleEl.dom.innerHTML = str;
30579     },
30580      
30581      /**
30582      * Set the Body Message HTML
30583      * @param {String} html
30584      */
30585     setHtml : function(str)
30586     {
30587         this.htmlEl.dom.innerHTML = str;
30588     },
30589     /**
30590      * Set the Weight of the alert
30591      * @param {String} (success|info|warning|danger) weight
30592      */
30593     
30594     setWeight : function(weight)
30595     {
30596         if(this.weight){
30597             this.el.removeClass('alert-' + this.weight);
30598         }
30599         
30600         this.weight = weight;
30601         
30602         this.el.addClass('alert-' + this.weight);
30603     },
30604       /**
30605      * Set the Icon of the alert
30606      * @param {String} see fontawsome names (name without the 'fa-' bit)
30607      */
30608     setIcon : function(icon)
30609     {
30610         if(this.faicon){
30611             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30612         }
30613         
30614         this.faicon = icon;
30615         
30616         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30617     },
30618     /**
30619      * Hide the Alert
30620      */
30621     hide: function() 
30622     {
30623         this.el.hide();   
30624     },
30625     /**
30626      * Show the Alert
30627      */
30628     show: function() 
30629     {  
30630         this.el.show();   
30631     }
30632     
30633 });
30634
30635  
30636 /*
30637 * Licence: LGPL
30638 */
30639
30640 /**
30641  * @class Roo.bootstrap.UploadCropbox
30642  * @extends Roo.bootstrap.Component
30643  * Bootstrap UploadCropbox class
30644  * @cfg {String} emptyText show when image has been loaded
30645  * @cfg {String} rotateNotify show when image too small to rotate
30646  * @cfg {Number} errorTimeout default 3000
30647  * @cfg {Number} minWidth default 300
30648  * @cfg {Number} minHeight default 300
30649  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30650  * @cfg {Boolean} isDocument (true|false) default false
30651  * @cfg {String} url action url
30652  * @cfg {String} paramName default 'imageUpload'
30653  * @cfg {String} method default POST
30654  * @cfg {Boolean} loadMask (true|false) default true
30655  * @cfg {Boolean} loadingText default 'Loading...'
30656  * 
30657  * @constructor
30658  * Create a new UploadCropbox
30659  * @param {Object} config The config object
30660  */
30661
30662 Roo.bootstrap.UploadCropbox = function(config){
30663     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30664     
30665     this.addEvents({
30666         /**
30667          * @event beforeselectfile
30668          * Fire before select file
30669          * @param {Roo.bootstrap.UploadCropbox} this
30670          */
30671         "beforeselectfile" : true,
30672         /**
30673          * @event initial
30674          * Fire after initEvent
30675          * @param {Roo.bootstrap.UploadCropbox} this
30676          */
30677         "initial" : true,
30678         /**
30679          * @event crop
30680          * Fire after initEvent
30681          * @param {Roo.bootstrap.UploadCropbox} this
30682          * @param {String} data
30683          */
30684         "crop" : true,
30685         /**
30686          * @event prepare
30687          * Fire when preparing the file data
30688          * @param {Roo.bootstrap.UploadCropbox} this
30689          * @param {Object} file
30690          */
30691         "prepare" : true,
30692         /**
30693          * @event exception
30694          * Fire when get exception
30695          * @param {Roo.bootstrap.UploadCropbox} this
30696          * @param {XMLHttpRequest} xhr
30697          */
30698         "exception" : true,
30699         /**
30700          * @event beforeloadcanvas
30701          * Fire before load the canvas
30702          * @param {Roo.bootstrap.UploadCropbox} this
30703          * @param {String} src
30704          */
30705         "beforeloadcanvas" : true,
30706         /**
30707          * @event trash
30708          * Fire when trash image
30709          * @param {Roo.bootstrap.UploadCropbox} this
30710          */
30711         "trash" : true,
30712         /**
30713          * @event download
30714          * Fire when download the image
30715          * @param {Roo.bootstrap.UploadCropbox} this
30716          */
30717         "download" : true,
30718         /**
30719          * @event footerbuttonclick
30720          * Fire when footerbuttonclick
30721          * @param {Roo.bootstrap.UploadCropbox} this
30722          * @param {String} type
30723          */
30724         "footerbuttonclick" : true,
30725         /**
30726          * @event resize
30727          * Fire when resize
30728          * @param {Roo.bootstrap.UploadCropbox} this
30729          */
30730         "resize" : true,
30731         /**
30732          * @event rotate
30733          * Fire when rotate the image
30734          * @param {Roo.bootstrap.UploadCropbox} this
30735          * @param {String} pos
30736          */
30737         "rotate" : true,
30738         /**
30739          * @event inspect
30740          * Fire when inspect the file
30741          * @param {Roo.bootstrap.UploadCropbox} this
30742          * @param {Object} file
30743          */
30744         "inspect" : true,
30745         /**
30746          * @event upload
30747          * Fire when xhr upload the file
30748          * @param {Roo.bootstrap.UploadCropbox} this
30749          * @param {Object} data
30750          */
30751         "upload" : true,
30752         /**
30753          * @event arrange
30754          * Fire when arrange the file data
30755          * @param {Roo.bootstrap.UploadCropbox} this
30756          * @param {Object} formData
30757          */
30758         "arrange" : true
30759     });
30760     
30761     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30762 };
30763
30764 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30765     
30766     emptyText : 'Click to upload image',
30767     rotateNotify : 'Image is too small to rotate',
30768     errorTimeout : 3000,
30769     scale : 0,
30770     baseScale : 1,
30771     rotate : 0,
30772     dragable : false,
30773     pinching : false,
30774     mouseX : 0,
30775     mouseY : 0,
30776     cropData : false,
30777     minWidth : 300,
30778     minHeight : 300,
30779     file : false,
30780     exif : {},
30781     baseRotate : 1,
30782     cropType : 'image/jpeg',
30783     buttons : false,
30784     canvasLoaded : false,
30785     isDocument : false,
30786     method : 'POST',
30787     paramName : 'imageUpload',
30788     loadMask : true,
30789     loadingText : 'Loading...',
30790     maskEl : false,
30791     
30792     getAutoCreate : function()
30793     {
30794         var cfg = {
30795             tag : 'div',
30796             cls : 'roo-upload-cropbox',
30797             cn : [
30798                 {
30799                     tag : 'input',
30800                     cls : 'roo-upload-cropbox-selector',
30801                     type : 'file'
30802                 },
30803                 {
30804                     tag : 'div',
30805                     cls : 'roo-upload-cropbox-body',
30806                     style : 'cursor:pointer',
30807                     cn : [
30808                         {
30809                             tag : 'div',
30810                             cls : 'roo-upload-cropbox-preview'
30811                         },
30812                         {
30813                             tag : 'div',
30814                             cls : 'roo-upload-cropbox-thumb'
30815                         },
30816                         {
30817                             tag : 'div',
30818                             cls : 'roo-upload-cropbox-empty-notify',
30819                             html : this.emptyText
30820                         },
30821                         {
30822                             tag : 'div',
30823                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30824                             html : this.rotateNotify
30825                         }
30826                     ]
30827                 },
30828                 {
30829                     tag : 'div',
30830                     cls : 'roo-upload-cropbox-footer',
30831                     cn : {
30832                         tag : 'div',
30833                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30834                         cn : []
30835                     }
30836                 }
30837             ]
30838         };
30839         
30840         return cfg;
30841     },
30842     
30843     onRender : function(ct, position)
30844     {
30845         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30846         
30847         if (this.buttons.length) {
30848             
30849             Roo.each(this.buttons, function(bb) {
30850                 
30851                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30852                 
30853                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30854                 
30855             }, this);
30856         }
30857         
30858         if(this.loadMask){
30859             this.maskEl = this.el;
30860         }
30861     },
30862     
30863     initEvents : function()
30864     {
30865         this.urlAPI = (window.createObjectURL && window) || 
30866                                 (window.URL && URL.revokeObjectURL && URL) || 
30867                                 (window.webkitURL && webkitURL);
30868                         
30869         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30870         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30871         
30872         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30873         this.selectorEl.hide();
30874         
30875         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30876         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30877         
30878         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30879         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30880         this.thumbEl.hide();
30881         
30882         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30883         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30884         
30885         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30886         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30887         this.errorEl.hide();
30888         
30889         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30890         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30891         this.footerEl.hide();
30892         
30893         this.setThumbBoxSize();
30894         
30895         this.bind();
30896         
30897         this.resize();
30898         
30899         this.fireEvent('initial', this);
30900     },
30901
30902     bind : function()
30903     {
30904         var _this = this;
30905         
30906         window.addEventListener("resize", function() { _this.resize(); } );
30907         
30908         this.bodyEl.on('click', this.beforeSelectFile, this);
30909         
30910         if(Roo.isTouch){
30911             this.bodyEl.on('touchstart', this.onTouchStart, this);
30912             this.bodyEl.on('touchmove', this.onTouchMove, this);
30913             this.bodyEl.on('touchend', this.onTouchEnd, this);
30914         }
30915         
30916         if(!Roo.isTouch){
30917             this.bodyEl.on('mousedown', this.onMouseDown, this);
30918             this.bodyEl.on('mousemove', this.onMouseMove, this);
30919             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30920             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30921             Roo.get(document).on('mouseup', this.onMouseUp, this);
30922         }
30923         
30924         this.selectorEl.on('change', this.onFileSelected, this);
30925     },
30926     
30927     reset : function()
30928     {    
30929         this.scale = 0;
30930         this.baseScale = 1;
30931         this.rotate = 0;
30932         this.baseRotate = 1;
30933         this.dragable = false;
30934         this.pinching = false;
30935         this.mouseX = 0;
30936         this.mouseY = 0;
30937         this.cropData = false;
30938         this.notifyEl.dom.innerHTML = this.emptyText;
30939         
30940         this.selectorEl.dom.value = '';
30941         
30942     },
30943     
30944     resize : function()
30945     {
30946         if(this.fireEvent('resize', this) != false){
30947             this.setThumbBoxPosition();
30948             this.setCanvasPosition();
30949         }
30950     },
30951     
30952     onFooterButtonClick : function(e, el, o, type)
30953     {
30954         switch (type) {
30955             case 'rotate-left' :
30956                 this.onRotateLeft(e);
30957                 break;
30958             case 'rotate-right' :
30959                 this.onRotateRight(e);
30960                 break;
30961             case 'picture' :
30962                 this.beforeSelectFile(e);
30963                 break;
30964             case 'trash' :
30965                 this.trash(e);
30966                 break;
30967             case 'crop' :
30968                 this.crop(e);
30969                 break;
30970             case 'download' :
30971                 this.download(e);
30972                 break;
30973             default :
30974                 break;
30975         }
30976         
30977         this.fireEvent('footerbuttonclick', this, type);
30978     },
30979     
30980     beforeSelectFile : function(e)
30981     {
30982         e.preventDefault();
30983         
30984         if(this.fireEvent('beforeselectfile', this) != false){
30985             this.selectorEl.dom.click();
30986         }
30987     },
30988     
30989     onFileSelected : function(e)
30990     {
30991         e.preventDefault();
30992         
30993         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30994             return;
30995         }
30996         
30997         var file = this.selectorEl.dom.files[0];
30998         
30999         if(this.fireEvent('inspect', this, file) != false){
31000             this.prepare(file);
31001         }
31002         
31003     },
31004     
31005     trash : function(e)
31006     {
31007         this.fireEvent('trash', this);
31008     },
31009     
31010     download : function(e)
31011     {
31012         this.fireEvent('download', this);
31013     },
31014     
31015     loadCanvas : function(src)
31016     {   
31017         if(this.fireEvent('beforeloadcanvas', this, src) != false){
31018             
31019             this.reset();
31020             
31021             this.imageEl = document.createElement('img');
31022             
31023             var _this = this;
31024             
31025             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31026             
31027             this.imageEl.src = src;
31028         }
31029     },
31030     
31031     onLoadCanvas : function()
31032     {   
31033         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31034         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31035         
31036         this.bodyEl.un('click', this.beforeSelectFile, this);
31037         
31038         this.notifyEl.hide();
31039         this.thumbEl.show();
31040         this.footerEl.show();
31041         
31042         this.baseRotateLevel();
31043         
31044         if(this.isDocument){
31045             this.setThumbBoxSize();
31046         }
31047         
31048         this.setThumbBoxPosition();
31049         
31050         this.baseScaleLevel();
31051         
31052         this.draw();
31053         
31054         this.resize();
31055         
31056         this.canvasLoaded = true;
31057         
31058         if(this.loadMask){
31059             this.maskEl.unmask();
31060         }
31061         
31062     },
31063     
31064     setCanvasPosition : function()
31065     {   
31066         if(!this.canvasEl){
31067             return;
31068         }
31069         
31070         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31071         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31072         
31073         this.previewEl.setLeft(pw);
31074         this.previewEl.setTop(ph);
31075         
31076     },
31077     
31078     onMouseDown : function(e)
31079     {   
31080         e.stopEvent();
31081         
31082         this.dragable = true;
31083         this.pinching = false;
31084         
31085         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31086             this.dragable = false;
31087             return;
31088         }
31089         
31090         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31091         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31092         
31093     },
31094     
31095     onMouseMove : function(e)
31096     {   
31097         e.stopEvent();
31098         
31099         if(!this.canvasLoaded){
31100             return;
31101         }
31102         
31103         if (!this.dragable){
31104             return;
31105         }
31106         
31107         var minX = Math.ceil(this.thumbEl.getLeft(true));
31108         var minY = Math.ceil(this.thumbEl.getTop(true));
31109         
31110         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31111         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31112         
31113         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31114         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31115         
31116         x = x - this.mouseX;
31117         y = y - this.mouseY;
31118         
31119         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31120         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31121         
31122         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31123         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31124         
31125         this.previewEl.setLeft(bgX);
31126         this.previewEl.setTop(bgY);
31127         
31128         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31129         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31130     },
31131     
31132     onMouseUp : function(e)
31133     {   
31134         e.stopEvent();
31135         
31136         this.dragable = false;
31137     },
31138     
31139     onMouseWheel : function(e)
31140     {   
31141         e.stopEvent();
31142         
31143         this.startScale = this.scale;
31144         
31145         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31146         
31147         if(!this.zoomable()){
31148             this.scale = this.startScale;
31149             return;
31150         }
31151         
31152         this.draw();
31153         
31154         return;
31155     },
31156     
31157     zoomable : function()
31158     {
31159         var minScale = this.thumbEl.getWidth() / this.minWidth;
31160         
31161         if(this.minWidth < this.minHeight){
31162             minScale = this.thumbEl.getHeight() / this.minHeight;
31163         }
31164         
31165         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31166         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31167         
31168         if(
31169                 this.isDocument &&
31170                 (this.rotate == 0 || this.rotate == 180) && 
31171                 (
31172                     width > this.imageEl.OriginWidth || 
31173                     height > this.imageEl.OriginHeight ||
31174                     (width < this.minWidth && height < this.minHeight)
31175                 )
31176         ){
31177             return false;
31178         }
31179         
31180         if(
31181                 this.isDocument &&
31182                 (this.rotate == 90 || this.rotate == 270) && 
31183                 (
31184                     width > this.imageEl.OriginWidth || 
31185                     height > this.imageEl.OriginHeight ||
31186                     (width < this.minHeight && height < this.minWidth)
31187                 )
31188         ){
31189             return false;
31190         }
31191         
31192         if(
31193                 !this.isDocument &&
31194                 (this.rotate == 0 || this.rotate == 180) && 
31195                 (
31196                     width < this.minWidth || 
31197                     width > this.imageEl.OriginWidth || 
31198                     height < this.minHeight || 
31199                     height > this.imageEl.OriginHeight
31200                 )
31201         ){
31202             return false;
31203         }
31204         
31205         if(
31206                 !this.isDocument &&
31207                 (this.rotate == 90 || this.rotate == 270) && 
31208                 (
31209                     width < this.minHeight || 
31210                     width > this.imageEl.OriginWidth || 
31211                     height < this.minWidth || 
31212                     height > this.imageEl.OriginHeight
31213                 )
31214         ){
31215             return false;
31216         }
31217         
31218         return true;
31219         
31220     },
31221     
31222     onRotateLeft : function(e)
31223     {   
31224         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31225             
31226             var minScale = this.thumbEl.getWidth() / this.minWidth;
31227             
31228             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31229             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31230             
31231             this.startScale = this.scale;
31232             
31233             while (this.getScaleLevel() < minScale){
31234             
31235                 this.scale = this.scale + 1;
31236                 
31237                 if(!this.zoomable()){
31238                     break;
31239                 }
31240                 
31241                 if(
31242                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31243                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31244                 ){
31245                     continue;
31246                 }
31247                 
31248                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31249
31250                 this.draw();
31251                 
31252                 return;
31253             }
31254             
31255             this.scale = this.startScale;
31256             
31257             this.onRotateFail();
31258             
31259             return false;
31260         }
31261         
31262         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31263
31264         if(this.isDocument){
31265             this.setThumbBoxSize();
31266             this.setThumbBoxPosition();
31267             this.setCanvasPosition();
31268         }
31269         
31270         this.draw();
31271         
31272         this.fireEvent('rotate', this, 'left');
31273         
31274     },
31275     
31276     onRotateRight : function(e)
31277     {
31278         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31279             
31280             var minScale = this.thumbEl.getWidth() / this.minWidth;
31281         
31282             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31283             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31284             
31285             this.startScale = this.scale;
31286             
31287             while (this.getScaleLevel() < minScale){
31288             
31289                 this.scale = this.scale + 1;
31290                 
31291                 if(!this.zoomable()){
31292                     break;
31293                 }
31294                 
31295                 if(
31296                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31297                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31298                 ){
31299                     continue;
31300                 }
31301                 
31302                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31303
31304                 this.draw();
31305                 
31306                 return;
31307             }
31308             
31309             this.scale = this.startScale;
31310             
31311             this.onRotateFail();
31312             
31313             return false;
31314         }
31315         
31316         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31317
31318         if(this.isDocument){
31319             this.setThumbBoxSize();
31320             this.setThumbBoxPosition();
31321             this.setCanvasPosition();
31322         }
31323         
31324         this.draw();
31325         
31326         this.fireEvent('rotate', this, 'right');
31327     },
31328     
31329     onRotateFail : function()
31330     {
31331         this.errorEl.show(true);
31332         
31333         var _this = this;
31334         
31335         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31336     },
31337     
31338     draw : function()
31339     {
31340         this.previewEl.dom.innerHTML = '';
31341         
31342         var canvasEl = document.createElement("canvas");
31343         
31344         var contextEl = canvasEl.getContext("2d");
31345         
31346         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31347         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31348         var center = this.imageEl.OriginWidth / 2;
31349         
31350         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31351             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31352             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31353             center = this.imageEl.OriginHeight / 2;
31354         }
31355         
31356         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31357         
31358         contextEl.translate(center, center);
31359         contextEl.rotate(this.rotate * Math.PI / 180);
31360
31361         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31362         
31363         this.canvasEl = document.createElement("canvas");
31364         
31365         this.contextEl = this.canvasEl.getContext("2d");
31366         
31367         switch (this.rotate) {
31368             case 0 :
31369                 
31370                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31371                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31372                 
31373                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31374                 
31375                 break;
31376             case 90 : 
31377                 
31378                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31379                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31380                 
31381                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31382                     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);
31383                     break;
31384                 }
31385                 
31386                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31387                 
31388                 break;
31389             case 180 :
31390                 
31391                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31392                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31393                 
31394                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31395                     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);
31396                     break;
31397                 }
31398                 
31399                 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);
31400                 
31401                 break;
31402             case 270 :
31403                 
31404                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31405                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31406         
31407                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31408                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31409                     break;
31410                 }
31411                 
31412                 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);
31413                 
31414                 break;
31415             default : 
31416                 break;
31417         }
31418         
31419         this.previewEl.appendChild(this.canvasEl);
31420         
31421         this.setCanvasPosition();
31422     },
31423     
31424     crop : function()
31425     {
31426         if(!this.canvasLoaded){
31427             return;
31428         }
31429         
31430         var imageCanvas = document.createElement("canvas");
31431         
31432         var imageContext = imageCanvas.getContext("2d");
31433         
31434         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31435         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31436         
31437         var center = imageCanvas.width / 2;
31438         
31439         imageContext.translate(center, center);
31440         
31441         imageContext.rotate(this.rotate * Math.PI / 180);
31442         
31443         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31444         
31445         var canvas = document.createElement("canvas");
31446         
31447         var context = canvas.getContext("2d");
31448                 
31449         canvas.width = this.minWidth;
31450         canvas.height = this.minHeight;
31451
31452         switch (this.rotate) {
31453             case 0 :
31454                 
31455                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31456                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31457                 
31458                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31459                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31460                 
31461                 var targetWidth = this.minWidth - 2 * x;
31462                 var targetHeight = this.minHeight - 2 * y;
31463                 
31464                 var scale = 1;
31465                 
31466                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31467                     scale = targetWidth / width;
31468                 }
31469                 
31470                 if(x > 0 && y == 0){
31471                     scale = targetHeight / height;
31472                 }
31473                 
31474                 if(x > 0 && y > 0){
31475                     scale = targetWidth / width;
31476                     
31477                     if(width < height){
31478                         scale = targetHeight / height;
31479                     }
31480                 }
31481                 
31482                 context.scale(scale, scale);
31483                 
31484                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31485                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31486
31487                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31488                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31489
31490                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31491                 
31492                 break;
31493             case 90 : 
31494                 
31495                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31496                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31497                 
31498                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31499                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31500                 
31501                 var targetWidth = this.minWidth - 2 * x;
31502                 var targetHeight = this.minHeight - 2 * y;
31503                 
31504                 var scale = 1;
31505                 
31506                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31507                     scale = targetWidth / width;
31508                 }
31509                 
31510                 if(x > 0 && y == 0){
31511                     scale = targetHeight / height;
31512                 }
31513                 
31514                 if(x > 0 && y > 0){
31515                     scale = targetWidth / width;
31516                     
31517                     if(width < height){
31518                         scale = targetHeight / height;
31519                     }
31520                 }
31521                 
31522                 context.scale(scale, scale);
31523                 
31524                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31525                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31526
31527                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31528                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31529                 
31530                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31531                 
31532                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31533                 
31534                 break;
31535             case 180 :
31536                 
31537                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31538                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31539                 
31540                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31541                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31542                 
31543                 var targetWidth = this.minWidth - 2 * x;
31544                 var targetHeight = this.minHeight - 2 * y;
31545                 
31546                 var scale = 1;
31547                 
31548                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31549                     scale = targetWidth / width;
31550                 }
31551                 
31552                 if(x > 0 && y == 0){
31553                     scale = targetHeight / height;
31554                 }
31555                 
31556                 if(x > 0 && y > 0){
31557                     scale = targetWidth / width;
31558                     
31559                     if(width < height){
31560                         scale = targetHeight / height;
31561                     }
31562                 }
31563                 
31564                 context.scale(scale, scale);
31565                 
31566                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31567                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31568
31569                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31570                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31571
31572                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31573                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31574                 
31575                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31576                 
31577                 break;
31578             case 270 :
31579                 
31580                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31581                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31582                 
31583                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31584                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31585                 
31586                 var targetWidth = this.minWidth - 2 * x;
31587                 var targetHeight = this.minHeight - 2 * y;
31588                 
31589                 var scale = 1;
31590                 
31591                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31592                     scale = targetWidth / width;
31593                 }
31594                 
31595                 if(x > 0 && y == 0){
31596                     scale = targetHeight / height;
31597                 }
31598                 
31599                 if(x > 0 && y > 0){
31600                     scale = targetWidth / width;
31601                     
31602                     if(width < height){
31603                         scale = targetHeight / height;
31604                     }
31605                 }
31606                 
31607                 context.scale(scale, scale);
31608                 
31609                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31610                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31611
31612                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31613                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31614                 
31615                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31616                 
31617                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31618                 
31619                 break;
31620             default : 
31621                 break;
31622         }
31623         
31624         this.cropData = canvas.toDataURL(this.cropType);
31625         
31626         if(this.fireEvent('crop', this, this.cropData) !== false){
31627             this.process(this.file, this.cropData);
31628         }
31629         
31630         return;
31631         
31632     },
31633     
31634     setThumbBoxSize : function()
31635     {
31636         var width, height;
31637         
31638         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31639             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31640             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31641             
31642             this.minWidth = width;
31643             this.minHeight = height;
31644             
31645             if(this.rotate == 90 || this.rotate == 270){
31646                 this.minWidth = height;
31647                 this.minHeight = width;
31648             }
31649         }
31650         
31651         height = 300;
31652         width = Math.ceil(this.minWidth * height / this.minHeight);
31653         
31654         if(this.minWidth > this.minHeight){
31655             width = 300;
31656             height = Math.ceil(this.minHeight * width / this.minWidth);
31657         }
31658         
31659         this.thumbEl.setStyle({
31660             width : width + 'px',
31661             height : height + 'px'
31662         });
31663
31664         return;
31665             
31666     },
31667     
31668     setThumbBoxPosition : function()
31669     {
31670         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31671         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31672         
31673         this.thumbEl.setLeft(x);
31674         this.thumbEl.setTop(y);
31675         
31676     },
31677     
31678     baseRotateLevel : function()
31679     {
31680         this.baseRotate = 1;
31681         
31682         if(
31683                 typeof(this.exif) != 'undefined' &&
31684                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31685                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31686         ){
31687             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31688         }
31689         
31690         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31691         
31692     },
31693     
31694     baseScaleLevel : function()
31695     {
31696         var width, height;
31697         
31698         if(this.isDocument){
31699             
31700             if(this.baseRotate == 6 || this.baseRotate == 8){
31701             
31702                 height = this.thumbEl.getHeight();
31703                 this.baseScale = height / this.imageEl.OriginWidth;
31704
31705                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31706                     width = this.thumbEl.getWidth();
31707                     this.baseScale = width / this.imageEl.OriginHeight;
31708                 }
31709
31710                 return;
31711             }
31712
31713             height = this.thumbEl.getHeight();
31714             this.baseScale = height / this.imageEl.OriginHeight;
31715
31716             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31717                 width = this.thumbEl.getWidth();
31718                 this.baseScale = width / this.imageEl.OriginWidth;
31719             }
31720
31721             return;
31722         }
31723         
31724         if(this.baseRotate == 6 || this.baseRotate == 8){
31725             
31726             width = this.thumbEl.getHeight();
31727             this.baseScale = width / this.imageEl.OriginHeight;
31728             
31729             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31730                 height = this.thumbEl.getWidth();
31731                 this.baseScale = height / this.imageEl.OriginHeight;
31732             }
31733             
31734             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31735                 height = this.thumbEl.getWidth();
31736                 this.baseScale = height / this.imageEl.OriginHeight;
31737                 
31738                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31739                     width = this.thumbEl.getHeight();
31740                     this.baseScale = width / this.imageEl.OriginWidth;
31741                 }
31742             }
31743             
31744             return;
31745         }
31746         
31747         width = this.thumbEl.getWidth();
31748         this.baseScale = width / this.imageEl.OriginWidth;
31749         
31750         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31751             height = this.thumbEl.getHeight();
31752             this.baseScale = height / this.imageEl.OriginHeight;
31753         }
31754         
31755         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31756             
31757             height = this.thumbEl.getHeight();
31758             this.baseScale = height / this.imageEl.OriginHeight;
31759             
31760             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31761                 width = this.thumbEl.getWidth();
31762                 this.baseScale = width / this.imageEl.OriginWidth;
31763             }
31764             
31765         }
31766         
31767         return;
31768     },
31769     
31770     getScaleLevel : function()
31771     {
31772         return this.baseScale * Math.pow(1.1, this.scale);
31773     },
31774     
31775     onTouchStart : function(e)
31776     {
31777         if(!this.canvasLoaded){
31778             this.beforeSelectFile(e);
31779             return;
31780         }
31781         
31782         var touches = e.browserEvent.touches;
31783         
31784         if(!touches){
31785             return;
31786         }
31787         
31788         if(touches.length == 1){
31789             this.onMouseDown(e);
31790             return;
31791         }
31792         
31793         if(touches.length != 2){
31794             return;
31795         }
31796         
31797         var coords = [];
31798         
31799         for(var i = 0, finger; finger = touches[i]; i++){
31800             coords.push(finger.pageX, finger.pageY);
31801         }
31802         
31803         var x = Math.pow(coords[0] - coords[2], 2);
31804         var y = Math.pow(coords[1] - coords[3], 2);
31805         
31806         this.startDistance = Math.sqrt(x + y);
31807         
31808         this.startScale = this.scale;
31809         
31810         this.pinching = true;
31811         this.dragable = false;
31812         
31813     },
31814     
31815     onTouchMove : function(e)
31816     {
31817         if(!this.pinching && !this.dragable){
31818             return;
31819         }
31820         
31821         var touches = e.browserEvent.touches;
31822         
31823         if(!touches){
31824             return;
31825         }
31826         
31827         if(this.dragable){
31828             this.onMouseMove(e);
31829             return;
31830         }
31831         
31832         var coords = [];
31833         
31834         for(var i = 0, finger; finger = touches[i]; i++){
31835             coords.push(finger.pageX, finger.pageY);
31836         }
31837         
31838         var x = Math.pow(coords[0] - coords[2], 2);
31839         var y = Math.pow(coords[1] - coords[3], 2);
31840         
31841         this.endDistance = Math.sqrt(x + y);
31842         
31843         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31844         
31845         if(!this.zoomable()){
31846             this.scale = this.startScale;
31847             return;
31848         }
31849         
31850         this.draw();
31851         
31852     },
31853     
31854     onTouchEnd : function(e)
31855     {
31856         this.pinching = false;
31857         this.dragable = false;
31858         
31859     },
31860     
31861     process : function(file, crop)
31862     {
31863         if(this.loadMask){
31864             this.maskEl.mask(this.loadingText);
31865         }
31866         
31867         this.xhr = new XMLHttpRequest();
31868         
31869         file.xhr = this.xhr;
31870
31871         this.xhr.open(this.method, this.url, true);
31872         
31873         var headers = {
31874             "Accept": "application/json",
31875             "Cache-Control": "no-cache",
31876             "X-Requested-With": "XMLHttpRequest"
31877         };
31878         
31879         for (var headerName in headers) {
31880             var headerValue = headers[headerName];
31881             if (headerValue) {
31882                 this.xhr.setRequestHeader(headerName, headerValue);
31883             }
31884         }
31885         
31886         var _this = this;
31887         
31888         this.xhr.onload = function()
31889         {
31890             _this.xhrOnLoad(_this.xhr);
31891         }
31892         
31893         this.xhr.onerror = function()
31894         {
31895             _this.xhrOnError(_this.xhr);
31896         }
31897         
31898         var formData = new FormData();
31899
31900         formData.append('returnHTML', 'NO');
31901         
31902         if(crop){
31903             formData.append('crop', crop);
31904         }
31905         
31906         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31907             formData.append(this.paramName, file, file.name);
31908         }
31909         
31910         if(typeof(file.filename) != 'undefined'){
31911             formData.append('filename', file.filename);
31912         }
31913         
31914         if(typeof(file.mimetype) != 'undefined'){
31915             formData.append('mimetype', file.mimetype);
31916         }
31917         
31918         if(this.fireEvent('arrange', this, formData) != false){
31919             this.xhr.send(formData);
31920         };
31921     },
31922     
31923     xhrOnLoad : function(xhr)
31924     {
31925         if(this.loadMask){
31926             this.maskEl.unmask();
31927         }
31928         
31929         if (xhr.readyState !== 4) {
31930             this.fireEvent('exception', this, xhr);
31931             return;
31932         }
31933
31934         var response = Roo.decode(xhr.responseText);
31935         
31936         if(!response.success){
31937             this.fireEvent('exception', this, xhr);
31938             return;
31939         }
31940         
31941         var response = Roo.decode(xhr.responseText);
31942         
31943         this.fireEvent('upload', this, response);
31944         
31945     },
31946     
31947     xhrOnError : function()
31948     {
31949         if(this.loadMask){
31950             this.maskEl.unmask();
31951         }
31952         
31953         Roo.log('xhr on error');
31954         
31955         var response = Roo.decode(xhr.responseText);
31956           
31957         Roo.log(response);
31958         
31959     },
31960     
31961     prepare : function(file)
31962     {   
31963         if(this.loadMask){
31964             this.maskEl.mask(this.loadingText);
31965         }
31966         
31967         this.file = false;
31968         this.exif = {};
31969         
31970         if(typeof(file) === 'string'){
31971             this.loadCanvas(file);
31972             return;
31973         }
31974         
31975         if(!file || !this.urlAPI){
31976             return;
31977         }
31978         
31979         this.file = file;
31980         this.cropType = file.type;
31981         
31982         var _this = this;
31983         
31984         if(this.fireEvent('prepare', this, this.file) != false){
31985             
31986             var reader = new FileReader();
31987             
31988             reader.onload = function (e) {
31989                 if (e.target.error) {
31990                     Roo.log(e.target.error);
31991                     return;
31992                 }
31993                 
31994                 var buffer = e.target.result,
31995                     dataView = new DataView(buffer),
31996                     offset = 2,
31997                     maxOffset = dataView.byteLength - 4,
31998                     markerBytes,
31999                     markerLength;
32000                 
32001                 if (dataView.getUint16(0) === 0xffd8) {
32002                     while (offset < maxOffset) {
32003                         markerBytes = dataView.getUint16(offset);
32004                         
32005                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32006                             markerLength = dataView.getUint16(offset + 2) + 2;
32007                             if (offset + markerLength > dataView.byteLength) {
32008                                 Roo.log('Invalid meta data: Invalid segment size.');
32009                                 break;
32010                             }
32011                             
32012                             if(markerBytes == 0xffe1){
32013                                 _this.parseExifData(
32014                                     dataView,
32015                                     offset,
32016                                     markerLength
32017                                 );
32018                             }
32019                             
32020                             offset += markerLength;
32021                             
32022                             continue;
32023                         }
32024                         
32025                         break;
32026                     }
32027                     
32028                 }
32029                 
32030                 var url = _this.urlAPI.createObjectURL(_this.file);
32031                 
32032                 _this.loadCanvas(url);
32033                 
32034                 return;
32035             }
32036             
32037             reader.readAsArrayBuffer(this.file);
32038             
32039         }
32040         
32041     },
32042     
32043     parseExifData : function(dataView, offset, length)
32044     {
32045         var tiffOffset = offset + 10,
32046             littleEndian,
32047             dirOffset;
32048     
32049         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32050             // No Exif data, might be XMP data instead
32051             return;
32052         }
32053         
32054         // Check for the ASCII code for "Exif" (0x45786966):
32055         if (dataView.getUint32(offset + 4) !== 0x45786966) {
32056             // No Exif data, might be XMP data instead
32057             return;
32058         }
32059         if (tiffOffset + 8 > dataView.byteLength) {
32060             Roo.log('Invalid Exif data: Invalid segment size.');
32061             return;
32062         }
32063         // Check for the two null bytes:
32064         if (dataView.getUint16(offset + 8) !== 0x0000) {
32065             Roo.log('Invalid Exif data: Missing byte alignment offset.');
32066             return;
32067         }
32068         // Check the byte alignment:
32069         switch (dataView.getUint16(tiffOffset)) {
32070         case 0x4949:
32071             littleEndian = true;
32072             break;
32073         case 0x4D4D:
32074             littleEndian = false;
32075             break;
32076         default:
32077             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32078             return;
32079         }
32080         // Check for the TIFF tag marker (0x002A):
32081         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32082             Roo.log('Invalid Exif data: Missing TIFF marker.');
32083             return;
32084         }
32085         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32086         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32087         
32088         this.parseExifTags(
32089             dataView,
32090             tiffOffset,
32091             tiffOffset + dirOffset,
32092             littleEndian
32093         );
32094     },
32095     
32096     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32097     {
32098         var tagsNumber,
32099             dirEndOffset,
32100             i;
32101         if (dirOffset + 6 > dataView.byteLength) {
32102             Roo.log('Invalid Exif data: Invalid directory offset.');
32103             return;
32104         }
32105         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32106         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32107         if (dirEndOffset + 4 > dataView.byteLength) {
32108             Roo.log('Invalid Exif data: Invalid directory size.');
32109             return;
32110         }
32111         for (i = 0; i < tagsNumber; i += 1) {
32112             this.parseExifTag(
32113                 dataView,
32114                 tiffOffset,
32115                 dirOffset + 2 + 12 * i, // tag offset
32116                 littleEndian
32117             );
32118         }
32119         // Return the offset to the next directory:
32120         return dataView.getUint32(dirEndOffset, littleEndian);
32121     },
32122     
32123     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32124     {
32125         var tag = dataView.getUint16(offset, littleEndian);
32126         
32127         this.exif[tag] = this.getExifValue(
32128             dataView,
32129             tiffOffset,
32130             offset,
32131             dataView.getUint16(offset + 2, littleEndian), // tag type
32132             dataView.getUint32(offset + 4, littleEndian), // tag length
32133             littleEndian
32134         );
32135     },
32136     
32137     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32138     {
32139         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32140             tagSize,
32141             dataOffset,
32142             values,
32143             i,
32144             str,
32145             c;
32146     
32147         if (!tagType) {
32148             Roo.log('Invalid Exif data: Invalid tag type.');
32149             return;
32150         }
32151         
32152         tagSize = tagType.size * length;
32153         // Determine if the value is contained in the dataOffset bytes,
32154         // or if the value at the dataOffset is a pointer to the actual data:
32155         dataOffset = tagSize > 4 ?
32156                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32157         if (dataOffset + tagSize > dataView.byteLength) {
32158             Roo.log('Invalid Exif data: Invalid data offset.');
32159             return;
32160         }
32161         if (length === 1) {
32162             return tagType.getValue(dataView, dataOffset, littleEndian);
32163         }
32164         values = [];
32165         for (i = 0; i < length; i += 1) {
32166             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32167         }
32168         
32169         if (tagType.ascii) {
32170             str = '';
32171             // Concatenate the chars:
32172             for (i = 0; i < values.length; i += 1) {
32173                 c = values[i];
32174                 // Ignore the terminating NULL byte(s):
32175                 if (c === '\u0000') {
32176                     break;
32177                 }
32178                 str += c;
32179             }
32180             return str;
32181         }
32182         return values;
32183     }
32184     
32185 });
32186
32187 Roo.apply(Roo.bootstrap.UploadCropbox, {
32188     tags : {
32189         'Orientation': 0x0112
32190     },
32191     
32192     Orientation: {
32193             1: 0, //'top-left',
32194 //            2: 'top-right',
32195             3: 180, //'bottom-right',
32196 //            4: 'bottom-left',
32197 //            5: 'left-top',
32198             6: 90, //'right-top',
32199 //            7: 'right-bottom',
32200             8: 270 //'left-bottom'
32201     },
32202     
32203     exifTagTypes : {
32204         // byte, 8-bit unsigned int:
32205         1: {
32206             getValue: function (dataView, dataOffset) {
32207                 return dataView.getUint8(dataOffset);
32208             },
32209             size: 1
32210         },
32211         // ascii, 8-bit byte:
32212         2: {
32213             getValue: function (dataView, dataOffset) {
32214                 return String.fromCharCode(dataView.getUint8(dataOffset));
32215             },
32216             size: 1,
32217             ascii: true
32218         },
32219         // short, 16 bit int:
32220         3: {
32221             getValue: function (dataView, dataOffset, littleEndian) {
32222                 return dataView.getUint16(dataOffset, littleEndian);
32223             },
32224             size: 2
32225         },
32226         // long, 32 bit int:
32227         4: {
32228             getValue: function (dataView, dataOffset, littleEndian) {
32229                 return dataView.getUint32(dataOffset, littleEndian);
32230             },
32231             size: 4
32232         },
32233         // rational = two long values, first is numerator, second is denominator:
32234         5: {
32235             getValue: function (dataView, dataOffset, littleEndian) {
32236                 return dataView.getUint32(dataOffset, littleEndian) /
32237                     dataView.getUint32(dataOffset + 4, littleEndian);
32238             },
32239             size: 8
32240         },
32241         // slong, 32 bit signed int:
32242         9: {
32243             getValue: function (dataView, dataOffset, littleEndian) {
32244                 return dataView.getInt32(dataOffset, littleEndian);
32245             },
32246             size: 4
32247         },
32248         // srational, two slongs, first is numerator, second is denominator:
32249         10: {
32250             getValue: function (dataView, dataOffset, littleEndian) {
32251                 return dataView.getInt32(dataOffset, littleEndian) /
32252                     dataView.getInt32(dataOffset + 4, littleEndian);
32253             },
32254             size: 8
32255         }
32256     },
32257     
32258     footer : {
32259         STANDARD : [
32260             {
32261                 tag : 'div',
32262                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32263                 action : 'rotate-left',
32264                 cn : [
32265                     {
32266                         tag : 'button',
32267                         cls : 'btn btn-default',
32268                         html : '<i class="fa fa-undo"></i>'
32269                     }
32270                 ]
32271             },
32272             {
32273                 tag : 'div',
32274                 cls : 'btn-group roo-upload-cropbox-picture',
32275                 action : 'picture',
32276                 cn : [
32277                     {
32278                         tag : 'button',
32279                         cls : 'btn btn-default',
32280                         html : '<i class="fa fa-picture-o"></i>'
32281                     }
32282                 ]
32283             },
32284             {
32285                 tag : 'div',
32286                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32287                 action : 'rotate-right',
32288                 cn : [
32289                     {
32290                         tag : 'button',
32291                         cls : 'btn btn-default',
32292                         html : '<i class="fa fa-repeat"></i>'
32293                     }
32294                 ]
32295             }
32296         ],
32297         DOCUMENT : [
32298             {
32299                 tag : 'div',
32300                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32301                 action : 'rotate-left',
32302                 cn : [
32303                     {
32304                         tag : 'button',
32305                         cls : 'btn btn-default',
32306                         html : '<i class="fa fa-undo"></i>'
32307                     }
32308                 ]
32309             },
32310             {
32311                 tag : 'div',
32312                 cls : 'btn-group roo-upload-cropbox-download',
32313                 action : 'download',
32314                 cn : [
32315                     {
32316                         tag : 'button',
32317                         cls : 'btn btn-default',
32318                         html : '<i class="fa fa-download"></i>'
32319                     }
32320                 ]
32321             },
32322             {
32323                 tag : 'div',
32324                 cls : 'btn-group roo-upload-cropbox-crop',
32325                 action : 'crop',
32326                 cn : [
32327                     {
32328                         tag : 'button',
32329                         cls : 'btn btn-default',
32330                         html : '<i class="fa fa-crop"></i>'
32331                     }
32332                 ]
32333             },
32334             {
32335                 tag : 'div',
32336                 cls : 'btn-group roo-upload-cropbox-trash',
32337                 action : 'trash',
32338                 cn : [
32339                     {
32340                         tag : 'button',
32341                         cls : 'btn btn-default',
32342                         html : '<i class="fa fa-trash"></i>'
32343                     }
32344                 ]
32345             },
32346             {
32347                 tag : 'div',
32348                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32349                 action : 'rotate-right',
32350                 cn : [
32351                     {
32352                         tag : 'button',
32353                         cls : 'btn btn-default',
32354                         html : '<i class="fa fa-repeat"></i>'
32355                     }
32356                 ]
32357             }
32358         ],
32359         ROTATOR : [
32360             {
32361                 tag : 'div',
32362                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32363                 action : 'rotate-left',
32364                 cn : [
32365                     {
32366                         tag : 'button',
32367                         cls : 'btn btn-default',
32368                         html : '<i class="fa fa-undo"></i>'
32369                     }
32370                 ]
32371             },
32372             {
32373                 tag : 'div',
32374                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32375                 action : 'rotate-right',
32376                 cn : [
32377                     {
32378                         tag : 'button',
32379                         cls : 'btn btn-default',
32380                         html : '<i class="fa fa-repeat"></i>'
32381                     }
32382                 ]
32383             }
32384         ]
32385     }
32386 });
32387
32388 /*
32389 * Licence: LGPL
32390 */
32391
32392 /**
32393  * @class Roo.bootstrap.DocumentManager
32394  * @extends Roo.bootstrap.Component
32395  * Bootstrap DocumentManager class
32396  * @cfg {String} paramName default 'imageUpload'
32397  * @cfg {String} toolTipName default 'filename'
32398  * @cfg {String} method default POST
32399  * @cfg {String} url action url
32400  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32401  * @cfg {Boolean} multiple multiple upload default true
32402  * @cfg {Number} thumbSize default 300
32403  * @cfg {String} fieldLabel
32404  * @cfg {Number} labelWidth default 4
32405  * @cfg {String} labelAlign (left|top) default left
32406  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32407 * @cfg {Number} labellg set the width of label (1-12)
32408  * @cfg {Number} labelmd set the width of label (1-12)
32409  * @cfg {Number} labelsm set the width of label (1-12)
32410  * @cfg {Number} labelxs set the width of label (1-12)
32411  * 
32412  * @constructor
32413  * Create a new DocumentManager
32414  * @param {Object} config The config object
32415  */
32416
32417 Roo.bootstrap.DocumentManager = function(config){
32418     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32419     
32420     this.files = [];
32421     this.delegates = [];
32422     
32423     this.addEvents({
32424         /**
32425          * @event initial
32426          * Fire when initial the DocumentManager
32427          * @param {Roo.bootstrap.DocumentManager} this
32428          */
32429         "initial" : true,
32430         /**
32431          * @event inspect
32432          * inspect selected file
32433          * @param {Roo.bootstrap.DocumentManager} this
32434          * @param {File} file
32435          */
32436         "inspect" : true,
32437         /**
32438          * @event exception
32439          * Fire when xhr load exception
32440          * @param {Roo.bootstrap.DocumentManager} this
32441          * @param {XMLHttpRequest} xhr
32442          */
32443         "exception" : true,
32444         /**
32445          * @event afterupload
32446          * Fire when xhr load exception
32447          * @param {Roo.bootstrap.DocumentManager} this
32448          * @param {XMLHttpRequest} xhr
32449          */
32450         "afterupload" : true,
32451         /**
32452          * @event prepare
32453          * prepare the form data
32454          * @param {Roo.bootstrap.DocumentManager} this
32455          * @param {Object} formData
32456          */
32457         "prepare" : true,
32458         /**
32459          * @event remove
32460          * Fire when remove the file
32461          * @param {Roo.bootstrap.DocumentManager} this
32462          * @param {Object} file
32463          */
32464         "remove" : true,
32465         /**
32466          * @event refresh
32467          * Fire after refresh the file
32468          * @param {Roo.bootstrap.DocumentManager} this
32469          */
32470         "refresh" : true,
32471         /**
32472          * @event click
32473          * Fire after click the image
32474          * @param {Roo.bootstrap.DocumentManager} this
32475          * @param {Object} file
32476          */
32477         "click" : true,
32478         /**
32479          * @event edit
32480          * Fire when upload a image and editable set to true
32481          * @param {Roo.bootstrap.DocumentManager} this
32482          * @param {Object} file
32483          */
32484         "edit" : true,
32485         /**
32486          * @event beforeselectfile
32487          * Fire before select file
32488          * @param {Roo.bootstrap.DocumentManager} this
32489          */
32490         "beforeselectfile" : true,
32491         /**
32492          * @event process
32493          * Fire before process file
32494          * @param {Roo.bootstrap.DocumentManager} this
32495          * @param {Object} file
32496          */
32497         "process" : true,
32498         /**
32499          * @event previewrendered
32500          * Fire when preview rendered
32501          * @param {Roo.bootstrap.DocumentManager} this
32502          * @param {Object} file
32503          */
32504         "previewrendered" : true,
32505         /**
32506          */
32507         "previewResize" : true
32508         
32509     });
32510 };
32511
32512 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32513     
32514     boxes : 0,
32515     inputName : '',
32516     thumbSize : 300,
32517     multiple : true,
32518     files : false,
32519     method : 'POST',
32520     url : '',
32521     paramName : 'imageUpload',
32522     toolTipName : 'filename',
32523     fieldLabel : '',
32524     labelWidth : 4,
32525     labelAlign : 'left',
32526     editable : true,
32527     delegates : false,
32528     xhr : false, 
32529     
32530     labellg : 0,
32531     labelmd : 0,
32532     labelsm : 0,
32533     labelxs : 0,
32534     
32535     getAutoCreate : function()
32536     {   
32537         var managerWidget = {
32538             tag : 'div',
32539             cls : 'roo-document-manager',
32540             cn : [
32541                 {
32542                     tag : 'input',
32543                     cls : 'roo-document-manager-selector',
32544                     type : 'file'
32545                 },
32546                 {
32547                     tag : 'div',
32548                     cls : 'roo-document-manager-uploader',
32549                     cn : [
32550                         {
32551                             tag : 'div',
32552                             cls : 'roo-document-manager-upload-btn',
32553                             html : '<i class="fa fa-plus"></i>'
32554                         }
32555                     ]
32556                     
32557                 }
32558             ]
32559         };
32560         
32561         var content = [
32562             {
32563                 tag : 'div',
32564                 cls : 'column col-md-12',
32565                 cn : managerWidget
32566             }
32567         ];
32568         
32569         if(this.fieldLabel.length){
32570             
32571             content = [
32572                 {
32573                     tag : 'div',
32574                     cls : 'column col-md-12',
32575                     html : this.fieldLabel
32576                 },
32577                 {
32578                     tag : 'div',
32579                     cls : 'column col-md-12',
32580                     cn : managerWidget
32581                 }
32582             ];
32583
32584             if(this.labelAlign == 'left'){
32585                 content = [
32586                     {
32587                         tag : 'div',
32588                         cls : 'column',
32589                         html : this.fieldLabel
32590                     },
32591                     {
32592                         tag : 'div',
32593                         cls : 'column',
32594                         cn : managerWidget
32595                     }
32596                 ];
32597                 
32598                 if(this.labelWidth > 12){
32599                     content[0].style = "width: " + this.labelWidth + 'px';
32600                 }
32601
32602                 if(this.labelWidth < 13 && this.labelmd == 0){
32603                     this.labelmd = this.labelWidth;
32604                 }
32605
32606                 if(this.labellg > 0){
32607                     content[0].cls += ' col-lg-' + this.labellg;
32608                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32609                 }
32610
32611                 if(this.labelmd > 0){
32612                     content[0].cls += ' col-md-' + this.labelmd;
32613                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32614                 }
32615
32616                 if(this.labelsm > 0){
32617                     content[0].cls += ' col-sm-' + this.labelsm;
32618                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32619                 }
32620
32621                 if(this.labelxs > 0){
32622                     content[0].cls += ' col-xs-' + this.labelxs;
32623                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32624                 }
32625                 
32626             }
32627         }
32628         
32629         var cfg = {
32630             tag : 'div',
32631             cls : 'row clearfix',
32632             cn : content
32633         };
32634         
32635         return cfg;
32636         
32637     },
32638     
32639     initEvents : function()
32640     {
32641         this.managerEl = this.el.select('.roo-document-manager', true).first();
32642         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32643         
32644         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32645         this.selectorEl.hide();
32646         
32647         if(this.multiple){
32648             this.selectorEl.attr('multiple', 'multiple');
32649         }
32650         
32651         this.selectorEl.on('change', this.onFileSelected, this);
32652         
32653         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32654         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32655         
32656         this.uploader.on('click', this.onUploaderClick, this);
32657         
32658         this.renderProgressDialog();
32659         
32660         var _this = this;
32661         
32662         window.addEventListener("resize", function() { _this.refresh(); } );
32663         
32664         this.fireEvent('initial', this);
32665     },
32666     
32667     renderProgressDialog : function()
32668     {
32669         var _this = this;
32670         
32671         this.progressDialog = new Roo.bootstrap.Modal({
32672             cls : 'roo-document-manager-progress-dialog',
32673             allow_close : false,
32674             animate : false,
32675             title : '',
32676             buttons : [
32677                 {
32678                     name  :'cancel',
32679                     weight : 'danger',
32680                     html : 'Cancel'
32681                 }
32682             ], 
32683             listeners : { 
32684                 btnclick : function() {
32685                     _this.uploadCancel();
32686                     this.hide();
32687                 }
32688             }
32689         });
32690          
32691         this.progressDialog.render(Roo.get(document.body));
32692          
32693         this.progress = new Roo.bootstrap.Progress({
32694             cls : 'roo-document-manager-progress',
32695             active : true,
32696             striped : true
32697         });
32698         
32699         this.progress.render(this.progressDialog.getChildContainer());
32700         
32701         this.progressBar = new Roo.bootstrap.ProgressBar({
32702             cls : 'roo-document-manager-progress-bar',
32703             aria_valuenow : 0,
32704             aria_valuemin : 0,
32705             aria_valuemax : 12,
32706             panel : 'success'
32707         });
32708         
32709         this.progressBar.render(this.progress.getChildContainer());
32710     },
32711     
32712     onUploaderClick : function(e)
32713     {
32714         e.preventDefault();
32715      
32716         if(this.fireEvent('beforeselectfile', this) != false){
32717             this.selectorEl.dom.click();
32718         }
32719         
32720     },
32721     
32722     onFileSelected : function(e)
32723     {
32724         e.preventDefault();
32725         
32726         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32727             return;
32728         }
32729         
32730         Roo.each(this.selectorEl.dom.files, function(file){
32731             if(this.fireEvent('inspect', this, file) != false){
32732                 this.files.push(file);
32733             }
32734         }, this);
32735         
32736         this.queue();
32737         
32738     },
32739     
32740     queue : function()
32741     {
32742         this.selectorEl.dom.value = '';
32743         
32744         if(!this.files || !this.files.length){
32745             return;
32746         }
32747         
32748         if(this.boxes > 0 && this.files.length > this.boxes){
32749             this.files = this.files.slice(0, this.boxes);
32750         }
32751         
32752         this.uploader.show();
32753         
32754         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32755             this.uploader.hide();
32756         }
32757         
32758         var _this = this;
32759         
32760         var files = [];
32761         
32762         var docs = [];
32763         
32764         Roo.each(this.files, function(file){
32765             
32766             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32767                 var f = this.renderPreview(file);
32768                 files.push(f);
32769                 return;
32770             }
32771             
32772             if(file.type.indexOf('image') != -1){
32773                 this.delegates.push(
32774                     (function(){
32775                         _this.process(file);
32776                     }).createDelegate(this)
32777                 );
32778         
32779                 return;
32780             }
32781             
32782             docs.push(
32783                 (function(){
32784                     _this.process(file);
32785                 }).createDelegate(this)
32786             );
32787             
32788         }, this);
32789         
32790         this.files = files;
32791         
32792         this.delegates = this.delegates.concat(docs);
32793         
32794         if(!this.delegates.length){
32795             this.refresh();
32796             return;
32797         }
32798         
32799         this.progressBar.aria_valuemax = this.delegates.length;
32800         
32801         this.arrange();
32802         
32803         return;
32804     },
32805     
32806     arrange : function()
32807     {
32808         if(!this.delegates.length){
32809             this.progressDialog.hide();
32810             this.refresh();
32811             return;
32812         }
32813         
32814         var delegate = this.delegates.shift();
32815         
32816         this.progressDialog.show();
32817         
32818         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32819         
32820         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32821         
32822         delegate();
32823     },
32824     
32825     refresh : function()
32826     {
32827         this.uploader.show();
32828         
32829         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32830             this.uploader.hide();
32831         }
32832         
32833         Roo.isTouch ? this.closable(false) : this.closable(true);
32834         
32835         this.fireEvent('refresh', this);
32836     },
32837     
32838     onRemove : function(e, el, o)
32839     {
32840         e.preventDefault();
32841         
32842         this.fireEvent('remove', this, o);
32843         
32844     },
32845     
32846     remove : function(o)
32847     {
32848         var files = [];
32849         
32850         Roo.each(this.files, function(file){
32851             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32852                 files.push(file);
32853                 return;
32854             }
32855
32856             o.target.remove();
32857
32858         }, this);
32859         
32860         this.files = files;
32861         
32862         this.refresh();
32863     },
32864     
32865     clear : function()
32866     {
32867         Roo.each(this.files, function(file){
32868             if(!file.target){
32869                 return;
32870             }
32871             
32872             file.target.remove();
32873
32874         }, this);
32875         
32876         this.files = [];
32877         
32878         this.refresh();
32879     },
32880     
32881     onClick : function(e, el, o)
32882     {
32883         e.preventDefault();
32884         
32885         this.fireEvent('click', this, o);
32886         
32887     },
32888     
32889     closable : function(closable)
32890     {
32891         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32892             
32893             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32894             
32895             if(closable){
32896                 el.show();
32897                 return;
32898             }
32899             
32900             el.hide();
32901             
32902         }, this);
32903     },
32904     
32905     xhrOnLoad : function(xhr)
32906     {
32907         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32908             el.remove();
32909         }, this);
32910         
32911         if (xhr.readyState !== 4) {
32912             this.arrange();
32913             this.fireEvent('exception', this, xhr);
32914             return;
32915         }
32916
32917         var response = Roo.decode(xhr.responseText);
32918         
32919         if(!response.success){
32920             this.arrange();
32921             this.fireEvent('exception', this, xhr);
32922             return;
32923         }
32924         
32925         var file = this.renderPreview(response.data);
32926         
32927         this.files.push(file);
32928         
32929         this.arrange();
32930         
32931         this.fireEvent('afterupload', this, xhr);
32932         
32933     },
32934     
32935     xhrOnError : function(xhr)
32936     {
32937         Roo.log('xhr on error');
32938         
32939         var response = Roo.decode(xhr.responseText);
32940           
32941         Roo.log(response);
32942         
32943         this.arrange();
32944     },
32945     
32946     process : function(file)
32947     {
32948         if(this.fireEvent('process', this, file) !== false){
32949             if(this.editable && file.type.indexOf('image') != -1){
32950                 this.fireEvent('edit', this, file);
32951                 return;
32952             }
32953
32954             this.uploadStart(file, false);
32955
32956             return;
32957         }
32958         
32959     },
32960     
32961     uploadStart : function(file, crop)
32962     {
32963         this.xhr = new XMLHttpRequest();
32964         
32965         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32966             this.arrange();
32967             return;
32968         }
32969         
32970         file.xhr = this.xhr;
32971             
32972         this.managerEl.createChild({
32973             tag : 'div',
32974             cls : 'roo-document-manager-loading',
32975             cn : [
32976                 {
32977                     tag : 'div',
32978                     tooltip : file.name,
32979                     cls : 'roo-document-manager-thumb',
32980                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32981                 }
32982             ]
32983
32984         });
32985
32986         this.xhr.open(this.method, this.url, true);
32987         
32988         var headers = {
32989             "Accept": "application/json",
32990             "Cache-Control": "no-cache",
32991             "X-Requested-With": "XMLHttpRequest"
32992         };
32993         
32994         for (var headerName in headers) {
32995             var headerValue = headers[headerName];
32996             if (headerValue) {
32997                 this.xhr.setRequestHeader(headerName, headerValue);
32998             }
32999         }
33000         
33001         var _this = this;
33002         
33003         this.xhr.onload = function()
33004         {
33005             _this.xhrOnLoad(_this.xhr);
33006         }
33007         
33008         this.xhr.onerror = function()
33009         {
33010             _this.xhrOnError(_this.xhr);
33011         }
33012         
33013         var formData = new FormData();
33014
33015         formData.append('returnHTML', 'NO');
33016         
33017         if(crop){
33018             formData.append('crop', crop);
33019         }
33020         
33021         formData.append(this.paramName, file, file.name);
33022         
33023         var options = {
33024             file : file, 
33025             manually : false
33026         };
33027         
33028         if(this.fireEvent('prepare', this, formData, options) != false){
33029             
33030             if(options.manually){
33031                 return;
33032             }
33033             
33034             this.xhr.send(formData);
33035             return;
33036         };
33037         
33038         this.uploadCancel();
33039     },
33040     
33041     uploadCancel : function()
33042     {
33043         if (this.xhr) {
33044             this.xhr.abort();
33045         }
33046         
33047         this.delegates = [];
33048         
33049         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33050             el.remove();
33051         }, this);
33052         
33053         this.arrange();
33054     },
33055     
33056     renderPreview : function(file)
33057     {
33058         if(typeof(file.target) != 'undefined' && file.target){
33059             return file;
33060         }
33061         
33062         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33063         
33064         var previewEl = this.managerEl.createChild({
33065             tag : 'div',
33066             cls : 'roo-document-manager-preview',
33067             cn : [
33068                 {
33069                     tag : 'div',
33070                     tooltip : file[this.toolTipName],
33071                     cls : 'roo-document-manager-thumb',
33072                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33073                 },
33074                 {
33075                     tag : 'button',
33076                     cls : 'close',
33077                     html : '<i class="fa fa-times-circle"></i>'
33078                 }
33079             ]
33080         });
33081
33082         var close = previewEl.select('button.close', true).first();
33083
33084         close.on('click', this.onRemove, this, file);
33085
33086         file.target = previewEl;
33087
33088         var image = previewEl.select('img', true).first();
33089         
33090         var _this = this;
33091         
33092         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33093         
33094         image.on('click', this.onClick, this, file);
33095         
33096         this.fireEvent('previewrendered', this, file);
33097         
33098         return file;
33099         
33100     },
33101     
33102     onPreviewLoad : function(file, image)
33103     {
33104         if(typeof(file.target) == 'undefined' || !file.target){
33105             return;
33106         }
33107         
33108         var width = image.dom.naturalWidth || image.dom.width;
33109         var height = image.dom.naturalHeight || image.dom.height;
33110         
33111         if(!this.previewResize) {
33112             return;
33113         }
33114         
33115         if(width > height){
33116             file.target.addClass('wide');
33117             return;
33118         }
33119         
33120         file.target.addClass('tall');
33121         return;
33122         
33123     },
33124     
33125     uploadFromSource : function(file, crop)
33126     {
33127         this.xhr = new XMLHttpRequest();
33128         
33129         this.managerEl.createChild({
33130             tag : 'div',
33131             cls : 'roo-document-manager-loading',
33132             cn : [
33133                 {
33134                     tag : 'div',
33135                     tooltip : file.name,
33136                     cls : 'roo-document-manager-thumb',
33137                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33138                 }
33139             ]
33140
33141         });
33142
33143         this.xhr.open(this.method, this.url, true);
33144         
33145         var headers = {
33146             "Accept": "application/json",
33147             "Cache-Control": "no-cache",
33148             "X-Requested-With": "XMLHttpRequest"
33149         };
33150         
33151         for (var headerName in headers) {
33152             var headerValue = headers[headerName];
33153             if (headerValue) {
33154                 this.xhr.setRequestHeader(headerName, headerValue);
33155             }
33156         }
33157         
33158         var _this = this;
33159         
33160         this.xhr.onload = function()
33161         {
33162             _this.xhrOnLoad(_this.xhr);
33163         }
33164         
33165         this.xhr.onerror = function()
33166         {
33167             _this.xhrOnError(_this.xhr);
33168         }
33169         
33170         var formData = new FormData();
33171
33172         formData.append('returnHTML', 'NO');
33173         
33174         formData.append('crop', crop);
33175         
33176         if(typeof(file.filename) != 'undefined'){
33177             formData.append('filename', file.filename);
33178         }
33179         
33180         if(typeof(file.mimetype) != 'undefined'){
33181             formData.append('mimetype', file.mimetype);
33182         }
33183         
33184         Roo.log(formData);
33185         
33186         if(this.fireEvent('prepare', this, formData) != false){
33187             this.xhr.send(formData);
33188         };
33189     }
33190 });
33191
33192 /*
33193 * Licence: LGPL
33194 */
33195
33196 /**
33197  * @class Roo.bootstrap.DocumentViewer
33198  * @extends Roo.bootstrap.Component
33199  * Bootstrap DocumentViewer class
33200  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33201  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33202  * 
33203  * @constructor
33204  * Create a new DocumentViewer
33205  * @param {Object} config The config object
33206  */
33207
33208 Roo.bootstrap.DocumentViewer = function(config){
33209     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33210     
33211     this.addEvents({
33212         /**
33213          * @event initial
33214          * Fire after initEvent
33215          * @param {Roo.bootstrap.DocumentViewer} this
33216          */
33217         "initial" : true,
33218         /**
33219          * @event click
33220          * Fire after click
33221          * @param {Roo.bootstrap.DocumentViewer} this
33222          */
33223         "click" : true,
33224         /**
33225          * @event download
33226          * Fire after download button
33227          * @param {Roo.bootstrap.DocumentViewer} this
33228          */
33229         "download" : true,
33230         /**
33231          * @event trash
33232          * Fire after trash button
33233          * @param {Roo.bootstrap.DocumentViewer} this
33234          */
33235         "trash" : true
33236         
33237     });
33238 };
33239
33240 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33241     
33242     showDownload : true,
33243     
33244     showTrash : true,
33245     
33246     getAutoCreate : function()
33247     {
33248         var cfg = {
33249             tag : 'div',
33250             cls : 'roo-document-viewer',
33251             cn : [
33252                 {
33253                     tag : 'div',
33254                     cls : 'roo-document-viewer-body',
33255                     cn : [
33256                         {
33257                             tag : 'div',
33258                             cls : 'roo-document-viewer-thumb',
33259                             cn : [
33260                                 {
33261                                     tag : 'img',
33262                                     cls : 'roo-document-viewer-image'
33263                                 }
33264                             ]
33265                         }
33266                     ]
33267                 },
33268                 {
33269                     tag : 'div',
33270                     cls : 'roo-document-viewer-footer',
33271                     cn : {
33272                         tag : 'div',
33273                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33274                         cn : [
33275                             {
33276                                 tag : 'div',
33277                                 cls : 'btn-group roo-document-viewer-download',
33278                                 cn : [
33279                                     {
33280                                         tag : 'button',
33281                                         cls : 'btn btn-default',
33282                                         html : '<i class="fa fa-download"></i>'
33283                                     }
33284                                 ]
33285                             },
33286                             {
33287                                 tag : 'div',
33288                                 cls : 'btn-group roo-document-viewer-trash',
33289                                 cn : [
33290                                     {
33291                                         tag : 'button',
33292                                         cls : 'btn btn-default',
33293                                         html : '<i class="fa fa-trash"></i>'
33294                                     }
33295                                 ]
33296                             }
33297                         ]
33298                     }
33299                 }
33300             ]
33301         };
33302         
33303         return cfg;
33304     },
33305     
33306     initEvents : function()
33307     {
33308         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33309         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33310         
33311         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33312         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33313         
33314         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33315         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33316         
33317         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33318         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33319         
33320         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33321         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33322         
33323         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33324         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33325         
33326         this.bodyEl.on('click', this.onClick, this);
33327         this.downloadBtn.on('click', this.onDownload, this);
33328         this.trashBtn.on('click', this.onTrash, this);
33329         
33330         this.downloadBtn.hide();
33331         this.trashBtn.hide();
33332         
33333         if(this.showDownload){
33334             this.downloadBtn.show();
33335         }
33336         
33337         if(this.showTrash){
33338             this.trashBtn.show();
33339         }
33340         
33341         if(!this.showDownload && !this.showTrash) {
33342             this.footerEl.hide();
33343         }
33344         
33345     },
33346     
33347     initial : function()
33348     {
33349         this.fireEvent('initial', this);
33350         
33351     },
33352     
33353     onClick : function(e)
33354     {
33355         e.preventDefault();
33356         
33357         this.fireEvent('click', this);
33358     },
33359     
33360     onDownload : function(e)
33361     {
33362         e.preventDefault();
33363         
33364         this.fireEvent('download', this);
33365     },
33366     
33367     onTrash : function(e)
33368     {
33369         e.preventDefault();
33370         
33371         this.fireEvent('trash', this);
33372     }
33373     
33374 });
33375 /*
33376  * - LGPL
33377  *
33378  * FieldLabel
33379  * 
33380  */
33381
33382 /**
33383  * @class Roo.bootstrap.form.FieldLabel
33384  * @extends Roo.bootstrap.Component
33385  * Bootstrap FieldLabel class
33386  * @cfg {String} html contents of the element
33387  * @cfg {String} tag tag of the element default label
33388  * @cfg {String} cls class of the element
33389  * @cfg {String} target label target 
33390  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33391  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33392  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33393  * @cfg {String} iconTooltip default "This field is required"
33394  * @cfg {String} indicatorpos (left|right) default left
33395  * 
33396  * @constructor
33397  * Create a new FieldLabel
33398  * @param {Object} config The config object
33399  */
33400
33401 Roo.bootstrap.form.FieldLabel = function(config){
33402     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33403     
33404     this.addEvents({
33405             /**
33406              * @event invalid
33407              * Fires after the field has been marked as invalid.
33408              * @param {Roo.form.FieldLabel} this
33409              * @param {String} msg The validation message
33410              */
33411             invalid : true,
33412             /**
33413              * @event valid
33414              * Fires after the field has been validated with no errors.
33415              * @param {Roo.form.FieldLabel} this
33416              */
33417             valid : true
33418         });
33419 };
33420
33421 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
33422     
33423     tag: 'label',
33424     cls: '',
33425     html: '',
33426     target: '',
33427     allowBlank : true,
33428     invalidClass : 'has-warning',
33429     validClass : 'has-success',
33430     iconTooltip : 'This field is required',
33431     indicatorpos : 'left',
33432     
33433     getAutoCreate : function(){
33434         
33435         var cls = "";
33436         if (!this.allowBlank) {
33437             cls  = "visible";
33438         }
33439         
33440         var cfg = {
33441             tag : this.tag,
33442             cls : 'roo-bootstrap-field-label ' + this.cls,
33443             for : this.target,
33444             cn : [
33445                 {
33446                     tag : 'i',
33447                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33448                     tooltip : this.iconTooltip
33449                 },
33450                 {
33451                     tag : 'span',
33452                     html : this.html
33453                 }
33454             ] 
33455         };
33456         
33457         if(this.indicatorpos == 'right'){
33458             var cfg = {
33459                 tag : this.tag,
33460                 cls : 'roo-bootstrap-field-label ' + this.cls,
33461                 for : this.target,
33462                 cn : [
33463                     {
33464                         tag : 'span',
33465                         html : this.html
33466                     },
33467                     {
33468                         tag : 'i',
33469                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33470                         tooltip : this.iconTooltip
33471                     }
33472                 ] 
33473             };
33474         }
33475         
33476         return cfg;
33477     },
33478     
33479     initEvents: function() 
33480     {
33481         Roo.bootstrap.Element.superclass.initEvents.call(this);
33482         
33483         this.indicator = this.indicatorEl();
33484         
33485         if(this.indicator){
33486             this.indicator.removeClass('visible');
33487             this.indicator.addClass('invisible');
33488         }
33489         
33490         Roo.bootstrap.form.FieldLabel.register(this);
33491     },
33492     
33493     indicatorEl : function()
33494     {
33495         var indicator = this.el.select('i.roo-required-indicator',true).first();
33496         
33497         if(!indicator){
33498             return false;
33499         }
33500         
33501         return indicator;
33502         
33503     },
33504     
33505     /**
33506      * Mark this field as valid
33507      */
33508     markValid : function()
33509     {
33510         if(this.indicator){
33511             this.indicator.removeClass('visible');
33512             this.indicator.addClass('invisible');
33513         }
33514         if (Roo.bootstrap.version == 3) {
33515             this.el.removeClass(this.invalidClass);
33516             this.el.addClass(this.validClass);
33517         } else {
33518             this.el.removeClass('is-invalid');
33519             this.el.addClass('is-valid');
33520         }
33521         
33522         
33523         this.fireEvent('valid', this);
33524     },
33525     
33526     /**
33527      * Mark this field as invalid
33528      * @param {String} msg The validation message
33529      */
33530     markInvalid : function(msg)
33531     {
33532         if(this.indicator){
33533             this.indicator.removeClass('invisible');
33534             this.indicator.addClass('visible');
33535         }
33536           if (Roo.bootstrap.version == 3) {
33537             this.el.removeClass(this.validClass);
33538             this.el.addClass(this.invalidClass);
33539         } else {
33540             this.el.removeClass('is-valid');
33541             this.el.addClass('is-invalid');
33542         }
33543         
33544         
33545         this.fireEvent('invalid', this, msg);
33546     }
33547     
33548    
33549 });
33550
33551 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33552     
33553     groups: {},
33554     
33555      /**
33556     * register a FieldLabel Group
33557     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33558     */
33559     register : function(label)
33560     {
33561         if(this.groups.hasOwnProperty(label.target)){
33562             return;
33563         }
33564      
33565         this.groups[label.target] = label;
33566         
33567     },
33568     /**
33569     * fetch a FieldLabel Group based on the target
33570     * @param {string} target
33571     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33572     */
33573     get: function(target) {
33574         if (typeof(this.groups[target]) == 'undefined') {
33575             return false;
33576         }
33577         
33578         return this.groups[target] ;
33579     }
33580 });
33581
33582  
33583
33584  /*
33585  * - LGPL
33586  *
33587  * page DateSplitField.
33588  * 
33589  */
33590
33591
33592 /**
33593  * @class Roo.bootstrap.form.DateSplitField
33594  * @extends Roo.bootstrap.Component
33595  * Bootstrap DateSplitField class
33596  * @cfg {string} fieldLabel - the label associated
33597  * @cfg {Number} labelWidth set the width of label (0-12)
33598  * @cfg {String} labelAlign (top|left)
33599  * @cfg {Boolean} dayAllowBlank (true|false) default false
33600  * @cfg {Boolean} monthAllowBlank (true|false) default false
33601  * @cfg {Boolean} yearAllowBlank (true|false) default false
33602  * @cfg {string} dayPlaceholder 
33603  * @cfg {string} monthPlaceholder
33604  * @cfg {string} yearPlaceholder
33605  * @cfg {string} dayFormat default 'd'
33606  * @cfg {string} monthFormat default 'm'
33607  * @cfg {string} yearFormat default 'Y'
33608  * @cfg {Number} labellg set the width of label (1-12)
33609  * @cfg {Number} labelmd set the width of label (1-12)
33610  * @cfg {Number} labelsm set the width of label (1-12)
33611  * @cfg {Number} labelxs set the width of label (1-12)
33612
33613  *     
33614  * @constructor
33615  * Create a new DateSplitField
33616  * @param {Object} config The config object
33617  */
33618
33619 Roo.bootstrap.form.DateSplitField = function(config){
33620     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33621     
33622     this.addEvents({
33623         // raw events
33624          /**
33625          * @event years
33626          * getting the data of years
33627          * @param {Roo.bootstrap.form.DateSplitField} this
33628          * @param {Object} years
33629          */
33630         "years" : true,
33631         /**
33632          * @event days
33633          * getting the data of days
33634          * @param {Roo.bootstrap.form.DateSplitField} this
33635          * @param {Object} days
33636          */
33637         "days" : true,
33638         /**
33639          * @event invalid
33640          * Fires after the field has been marked as invalid.
33641          * @param {Roo.form.Field} this
33642          * @param {String} msg The validation message
33643          */
33644         invalid : true,
33645        /**
33646          * @event valid
33647          * Fires after the field has been validated with no errors.
33648          * @param {Roo.form.Field} this
33649          */
33650         valid : true
33651     });
33652 };
33653
33654 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
33655     
33656     fieldLabel : '',
33657     labelAlign : 'top',
33658     labelWidth : 3,
33659     dayAllowBlank : false,
33660     monthAllowBlank : false,
33661     yearAllowBlank : false,
33662     dayPlaceholder : '',
33663     monthPlaceholder : '',
33664     yearPlaceholder : '',
33665     dayFormat : 'd',
33666     monthFormat : 'm',
33667     yearFormat : 'Y',
33668     isFormField : true,
33669     labellg : 0,
33670     labelmd : 0,
33671     labelsm : 0,
33672     labelxs : 0,
33673     
33674     getAutoCreate : function()
33675     {
33676         var cfg = {
33677             tag : 'div',
33678             cls : 'row roo-date-split-field-group',
33679             cn : [
33680                 {
33681                     tag : 'input',
33682                     type : 'hidden',
33683                     cls : 'form-hidden-field roo-date-split-field-group-value',
33684                     name : this.name
33685                 }
33686             ]
33687         };
33688         
33689         var labelCls = 'col-md-12';
33690         var contentCls = 'col-md-4';
33691         
33692         if(this.fieldLabel){
33693             
33694             var label = {
33695                 tag : 'div',
33696                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33697                 cn : [
33698                     {
33699                         tag : 'label',
33700                         html : this.fieldLabel
33701                     }
33702                 ]
33703             };
33704             
33705             if(this.labelAlign == 'left'){
33706             
33707                 if(this.labelWidth > 12){
33708                     label.style = "width: " + this.labelWidth + 'px';
33709                 }
33710
33711                 if(this.labelWidth < 13 && this.labelmd == 0){
33712                     this.labelmd = this.labelWidth;
33713                 }
33714
33715                 if(this.labellg > 0){
33716                     labelCls = ' col-lg-' + this.labellg;
33717                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33718                 }
33719
33720                 if(this.labelmd > 0){
33721                     labelCls = ' col-md-' + this.labelmd;
33722                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33723                 }
33724
33725                 if(this.labelsm > 0){
33726                     labelCls = ' col-sm-' + this.labelsm;
33727                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33728                 }
33729
33730                 if(this.labelxs > 0){
33731                     labelCls = ' col-xs-' + this.labelxs;
33732                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33733                 }
33734             }
33735             
33736             label.cls += ' ' + labelCls;
33737             
33738             cfg.cn.push(label);
33739         }
33740         
33741         Roo.each(['day', 'month', 'year'], function(t){
33742             cfg.cn.push({
33743                 tag : 'div',
33744                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33745             });
33746         }, this);
33747         
33748         return cfg;
33749     },
33750     
33751     inputEl: function ()
33752     {
33753         return this.el.select('.roo-date-split-field-group-value', true).first();
33754     },
33755     
33756     onRender : function(ct, position) 
33757     {
33758         var _this = this;
33759         
33760         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33761         
33762         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33763         
33764         this.dayField = new Roo.bootstrap.form.ComboBox({
33765             allowBlank : this.dayAllowBlank,
33766             alwaysQuery : true,
33767             displayField : 'value',
33768             editable : false,
33769             fieldLabel : '',
33770             forceSelection : true,
33771             mode : 'local',
33772             placeholder : this.dayPlaceholder,
33773             selectOnFocus : true,
33774             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33775             triggerAction : 'all',
33776             typeAhead : true,
33777             valueField : 'value',
33778             store : new Roo.data.SimpleStore({
33779                 data : (function() {    
33780                     var days = [];
33781                     _this.fireEvent('days', _this, days);
33782                     return days;
33783                 })(),
33784                 fields : [ 'value' ]
33785             }),
33786             listeners : {
33787                 select : function (_self, record, index)
33788                 {
33789                     _this.setValue(_this.getValue());
33790                 }
33791             }
33792         });
33793
33794         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33795         
33796         this.monthField = new Roo.bootstrap.form.MonthField({
33797             after : '<i class=\"fa fa-calendar\"></i>',
33798             allowBlank : this.monthAllowBlank,
33799             placeholder : this.monthPlaceholder,
33800             readOnly : true,
33801             listeners : {
33802                 render : function (_self)
33803                 {
33804                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33805                         e.preventDefault();
33806                         _self.focus();
33807                     });
33808                 },
33809                 select : function (_self, oldvalue, newvalue)
33810                 {
33811                     _this.setValue(_this.getValue());
33812                 }
33813             }
33814         });
33815         
33816         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33817         
33818         this.yearField = new Roo.bootstrap.form.ComboBox({
33819             allowBlank : this.yearAllowBlank,
33820             alwaysQuery : true,
33821             displayField : 'value',
33822             editable : false,
33823             fieldLabel : '',
33824             forceSelection : true,
33825             mode : 'local',
33826             placeholder : this.yearPlaceholder,
33827             selectOnFocus : true,
33828             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33829             triggerAction : 'all',
33830             typeAhead : true,
33831             valueField : 'value',
33832             store : new Roo.data.SimpleStore({
33833                 data : (function() {
33834                     var years = [];
33835                     _this.fireEvent('years', _this, years);
33836                     return years;
33837                 })(),
33838                 fields : [ 'value' ]
33839             }),
33840             listeners : {
33841                 select : function (_self, record, index)
33842                 {
33843                     _this.setValue(_this.getValue());
33844                 }
33845             }
33846         });
33847
33848         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33849     },
33850     
33851     setValue : function(v, format)
33852     {
33853         this.inputEl.dom.value = v;
33854         
33855         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33856         
33857         var d = Date.parseDate(v, f);
33858         
33859         if(!d){
33860             this.validate();
33861             return;
33862         }
33863         
33864         this.setDay(d.format(this.dayFormat));
33865         this.setMonth(d.format(this.monthFormat));
33866         this.setYear(d.format(this.yearFormat));
33867         
33868         this.validate();
33869         
33870         return;
33871     },
33872     
33873     setDay : function(v)
33874     {
33875         this.dayField.setValue(v);
33876         this.inputEl.dom.value = this.getValue();
33877         this.validate();
33878         return;
33879     },
33880     
33881     setMonth : function(v)
33882     {
33883         this.monthField.setValue(v, true);
33884         this.inputEl.dom.value = this.getValue();
33885         this.validate();
33886         return;
33887     },
33888     
33889     setYear : function(v)
33890     {
33891         this.yearField.setValue(v);
33892         this.inputEl.dom.value = this.getValue();
33893         this.validate();
33894         return;
33895     },
33896     
33897     getDay : function()
33898     {
33899         return this.dayField.getValue();
33900     },
33901     
33902     getMonth : function()
33903     {
33904         return this.monthField.getValue();
33905     },
33906     
33907     getYear : function()
33908     {
33909         return this.yearField.getValue();
33910     },
33911     
33912     getValue : function()
33913     {
33914         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33915         
33916         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33917         
33918         return date;
33919     },
33920     
33921     reset : function()
33922     {
33923         this.setDay('');
33924         this.setMonth('');
33925         this.setYear('');
33926         this.inputEl.dom.value = '';
33927         this.validate();
33928         return;
33929     },
33930     
33931     validate : function()
33932     {
33933         var d = this.dayField.validate();
33934         var m = this.monthField.validate();
33935         var y = this.yearField.validate();
33936         
33937         var valid = true;
33938         
33939         if(
33940                 (!this.dayAllowBlank && !d) ||
33941                 (!this.monthAllowBlank && !m) ||
33942                 (!this.yearAllowBlank && !y)
33943         ){
33944             valid = false;
33945         }
33946         
33947         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33948             return valid;
33949         }
33950         
33951         if(valid){
33952             this.markValid();
33953             return valid;
33954         }
33955         
33956         this.markInvalid();
33957         
33958         return valid;
33959     },
33960     
33961     markValid : function()
33962     {
33963         
33964         var label = this.el.select('label', true).first();
33965         var icon = this.el.select('i.fa-star', true).first();
33966
33967         if(label && icon){
33968             icon.remove();
33969         }
33970         
33971         this.fireEvent('valid', this);
33972     },
33973     
33974      /**
33975      * Mark this field as invalid
33976      * @param {String} msg The validation message
33977      */
33978     markInvalid : function(msg)
33979     {
33980         
33981         var label = this.el.select('label', true).first();
33982         var icon = this.el.select('i.fa-star', true).first();
33983
33984         if(label && !icon){
33985             this.el.select('.roo-date-split-field-label', true).createChild({
33986                 tag : 'i',
33987                 cls : 'text-danger fa fa-lg fa-star',
33988                 tooltip : 'This field is required',
33989                 style : 'margin-right:5px;'
33990             }, label, true);
33991         }
33992         
33993         this.fireEvent('invalid', this, msg);
33994     },
33995     
33996     clearInvalid : function()
33997     {
33998         var label = this.el.select('label', true).first();
33999         var icon = this.el.select('i.fa-star', true).first();
34000
34001         if(label && icon){
34002             icon.remove();
34003         }
34004         
34005         this.fireEvent('valid', this);
34006     },
34007     
34008     getName: function()
34009     {
34010         return this.name;
34011     }
34012     
34013 });
34014
34015  
34016
34017 /**
34018  * @class Roo.bootstrap.LayoutMasonry
34019  * @extends Roo.bootstrap.Component
34020  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34021  * Bootstrap Layout Masonry class
34022  *
34023  * This is based on 
34024  * http://masonry.desandro.com
34025  *
34026  * The idea is to render all the bricks based on vertical width...
34027  *
34028  * The original code extends 'outlayer' - we might need to use that....
34029
34030  * @constructor
34031  * Create a new Element
34032  * @param {Object} config The config object
34033  */
34034
34035 Roo.bootstrap.LayoutMasonry = function(config){
34036     
34037     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34038     
34039     this.bricks = [];
34040     
34041     Roo.bootstrap.LayoutMasonry.register(this);
34042     
34043     this.addEvents({
34044         // raw events
34045         /**
34046          * @event layout
34047          * Fire after layout the items
34048          * @param {Roo.bootstrap.LayoutMasonry} this
34049          * @param {Roo.EventObject} e
34050          */
34051         "layout" : true
34052     });
34053     
34054 };
34055
34056 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34057     
34058     /**
34059      * @cfg {Boolean} isLayoutInstant = no animation?
34060      */   
34061     isLayoutInstant : false, // needed?
34062    
34063     /**
34064      * @cfg {Number} boxWidth  width of the columns
34065      */   
34066     boxWidth : 450,
34067     
34068       /**
34069      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34070      */   
34071     boxHeight : 0,
34072     
34073     /**
34074      * @cfg {Number} padWidth padding below box..
34075      */   
34076     padWidth : 10, 
34077     
34078     /**
34079      * @cfg {Number} gutter gutter width..
34080      */   
34081     gutter : 10,
34082     
34083      /**
34084      * @cfg {Number} maxCols maximum number of columns
34085      */   
34086     
34087     maxCols: 0,
34088     
34089     /**
34090      * @cfg {Boolean} isAutoInitial defalut true
34091      */   
34092     isAutoInitial : true, 
34093     
34094     containerWidth: 0,
34095     
34096     /**
34097      * @cfg {Boolean} isHorizontal defalut false
34098      */   
34099     isHorizontal : false, 
34100
34101     currentSize : null,
34102     
34103     tag: 'div',
34104     
34105     cls: '',
34106     
34107     bricks: null, //CompositeElement
34108     
34109     cols : 1,
34110     
34111     _isLayoutInited : false,
34112     
34113 //    isAlternative : false, // only use for vertical layout...
34114     
34115     /**
34116      * @cfg {Number} alternativePadWidth padding below box..
34117      */   
34118     alternativePadWidth : 50,
34119     
34120     selectedBrick : [],
34121     
34122     getAutoCreate : function(){
34123         
34124         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34125         
34126         var cfg = {
34127             tag: this.tag,
34128             cls: 'blog-masonary-wrapper ' + this.cls,
34129             cn : {
34130                 cls : 'mas-boxes masonary'
34131             }
34132         };
34133         
34134         return cfg;
34135     },
34136     
34137     getChildContainer: function( )
34138     {
34139         if (this.boxesEl) {
34140             return this.boxesEl;
34141         }
34142         
34143         this.boxesEl = this.el.select('.mas-boxes').first();
34144         
34145         return this.boxesEl;
34146     },
34147     
34148     
34149     initEvents : function()
34150     {
34151         var _this = this;
34152         
34153         if(this.isAutoInitial){
34154             Roo.log('hook children rendered');
34155             this.on('childrenrendered', function() {
34156                 Roo.log('children rendered');
34157                 _this.initial();
34158             } ,this);
34159         }
34160     },
34161     
34162     initial : function()
34163     {
34164         this.selectedBrick = [];
34165         
34166         this.currentSize = this.el.getBox(true);
34167         
34168         Roo.EventManager.onWindowResize(this.resize, this); 
34169
34170         if(!this.isAutoInitial){
34171             this.layout();
34172             return;
34173         }
34174         
34175         this.layout();
34176         
34177         return;
34178         //this.layout.defer(500,this);
34179         
34180     },
34181     
34182     resize : function()
34183     {
34184         var cs = this.el.getBox(true);
34185         
34186         if (
34187                 this.currentSize.width == cs.width && 
34188                 this.currentSize.x == cs.x && 
34189                 this.currentSize.height == cs.height && 
34190                 this.currentSize.y == cs.y 
34191         ) {
34192             Roo.log("no change in with or X or Y");
34193             return;
34194         }
34195         
34196         this.currentSize = cs;
34197         
34198         this.layout();
34199         
34200     },
34201     
34202     layout : function()
34203     {   
34204         this._resetLayout();
34205         
34206         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34207         
34208         this.layoutItems( isInstant );
34209       
34210         this._isLayoutInited = true;
34211         
34212         this.fireEvent('layout', this);
34213         
34214     },
34215     
34216     _resetLayout : function()
34217     {
34218         if(this.isHorizontal){
34219             this.horizontalMeasureColumns();
34220             return;
34221         }
34222         
34223         this.verticalMeasureColumns();
34224         
34225     },
34226     
34227     verticalMeasureColumns : function()
34228     {
34229         this.getContainerWidth();
34230         
34231 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34232 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34233 //            return;
34234 //        }
34235         
34236         var boxWidth = this.boxWidth + this.padWidth;
34237         
34238         if(this.containerWidth < this.boxWidth){
34239             boxWidth = this.containerWidth
34240         }
34241         
34242         var containerWidth = this.containerWidth;
34243         
34244         var cols = Math.floor(containerWidth / boxWidth);
34245         
34246         this.cols = Math.max( cols, 1 );
34247         
34248         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34249         
34250         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34251         
34252         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34253         
34254         this.colWidth = boxWidth + avail - this.padWidth;
34255         
34256         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34257         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34258     },
34259     
34260     horizontalMeasureColumns : function()
34261     {
34262         this.getContainerWidth();
34263         
34264         var boxWidth = this.boxWidth;
34265         
34266         if(this.containerWidth < boxWidth){
34267             boxWidth = this.containerWidth;
34268         }
34269         
34270         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34271         
34272         this.el.setHeight(boxWidth);
34273         
34274     },
34275     
34276     getContainerWidth : function()
34277     {
34278         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34279     },
34280     
34281     layoutItems : function( isInstant )
34282     {
34283         Roo.log(this.bricks);
34284         
34285         var items = Roo.apply([], this.bricks);
34286         
34287         if(this.isHorizontal){
34288             this._horizontalLayoutItems( items , isInstant );
34289             return;
34290         }
34291         
34292 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34293 //            this._verticalAlternativeLayoutItems( items , isInstant );
34294 //            return;
34295 //        }
34296         
34297         this._verticalLayoutItems( items , isInstant );
34298         
34299     },
34300     
34301     _verticalLayoutItems : function ( items , isInstant)
34302     {
34303         if ( !items || !items.length ) {
34304             return;
34305         }
34306         
34307         var standard = [
34308             ['xs', 'xs', 'xs', 'tall'],
34309             ['xs', 'xs', 'tall'],
34310             ['xs', 'xs', 'sm'],
34311             ['xs', 'xs', 'xs'],
34312             ['xs', 'tall'],
34313             ['xs', 'sm'],
34314             ['xs', 'xs'],
34315             ['xs'],
34316             
34317             ['sm', 'xs', 'xs'],
34318             ['sm', 'xs'],
34319             ['sm'],
34320             
34321             ['tall', 'xs', 'xs', 'xs'],
34322             ['tall', 'xs', 'xs'],
34323             ['tall', 'xs'],
34324             ['tall']
34325             
34326         ];
34327         
34328         var queue = [];
34329         
34330         var boxes = [];
34331         
34332         var box = [];
34333         
34334         Roo.each(items, function(item, k){
34335             
34336             switch (item.size) {
34337                 // these layouts take up a full box,
34338                 case 'md' :
34339                 case 'md-left' :
34340                 case 'md-right' :
34341                 case 'wide' :
34342                     
34343                     if(box.length){
34344                         boxes.push(box);
34345                         box = [];
34346                     }
34347                     
34348                     boxes.push([item]);
34349                     
34350                     break;
34351                     
34352                 case 'xs' :
34353                 case 'sm' :
34354                 case 'tall' :
34355                     
34356                     box.push(item);
34357                     
34358                     break;
34359                 default :
34360                     break;
34361                     
34362             }
34363             
34364         }, this);
34365         
34366         if(box.length){
34367             boxes.push(box);
34368             box = [];
34369         }
34370         
34371         var filterPattern = function(box, length)
34372         {
34373             if(!box.length){
34374                 return;
34375             }
34376             
34377             var match = false;
34378             
34379             var pattern = box.slice(0, length);
34380             
34381             var format = [];
34382             
34383             Roo.each(pattern, function(i){
34384                 format.push(i.size);
34385             }, this);
34386             
34387             Roo.each(standard, function(s){
34388                 
34389                 if(String(s) != String(format)){
34390                     return;
34391                 }
34392                 
34393                 match = true;
34394                 return false;
34395                 
34396             }, this);
34397             
34398             if(!match && length == 1){
34399                 return;
34400             }
34401             
34402             if(!match){
34403                 filterPattern(box, length - 1);
34404                 return;
34405             }
34406                 
34407             queue.push(pattern);
34408
34409             box = box.slice(length, box.length);
34410
34411             filterPattern(box, 4);
34412
34413             return;
34414             
34415         }
34416         
34417         Roo.each(boxes, function(box, k){
34418             
34419             if(!box.length){
34420                 return;
34421             }
34422             
34423             if(box.length == 1){
34424                 queue.push(box);
34425                 return;
34426             }
34427             
34428             filterPattern(box, 4);
34429             
34430         }, this);
34431         
34432         this._processVerticalLayoutQueue( queue, isInstant );
34433         
34434     },
34435     
34436 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34437 //    {
34438 //        if ( !items || !items.length ) {
34439 //            return;
34440 //        }
34441 //
34442 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34443 //        
34444 //    },
34445     
34446     _horizontalLayoutItems : function ( items , isInstant)
34447     {
34448         if ( !items || !items.length || items.length < 3) {
34449             return;
34450         }
34451         
34452         items.reverse();
34453         
34454         var eItems = items.slice(0, 3);
34455         
34456         items = items.slice(3, items.length);
34457         
34458         var standard = [
34459             ['xs', 'xs', 'xs', 'wide'],
34460             ['xs', 'xs', 'wide'],
34461             ['xs', 'xs', 'sm'],
34462             ['xs', 'xs', 'xs'],
34463             ['xs', 'wide'],
34464             ['xs', 'sm'],
34465             ['xs', 'xs'],
34466             ['xs'],
34467             
34468             ['sm', 'xs', 'xs'],
34469             ['sm', 'xs'],
34470             ['sm'],
34471             
34472             ['wide', 'xs', 'xs', 'xs'],
34473             ['wide', 'xs', 'xs'],
34474             ['wide', 'xs'],
34475             ['wide'],
34476             
34477             ['wide-thin']
34478         ];
34479         
34480         var queue = [];
34481         
34482         var boxes = [];
34483         
34484         var box = [];
34485         
34486         Roo.each(items, function(item, k){
34487             
34488             switch (item.size) {
34489                 case 'md' :
34490                 case 'md-left' :
34491                 case 'md-right' :
34492                 case 'tall' :
34493                     
34494                     if(box.length){
34495                         boxes.push(box);
34496                         box = [];
34497                     }
34498                     
34499                     boxes.push([item]);
34500                     
34501                     break;
34502                     
34503                 case 'xs' :
34504                 case 'sm' :
34505                 case 'wide' :
34506                 case 'wide-thin' :
34507                     
34508                     box.push(item);
34509                     
34510                     break;
34511                 default :
34512                     break;
34513                     
34514             }
34515             
34516         }, this);
34517         
34518         if(box.length){
34519             boxes.push(box);
34520             box = [];
34521         }
34522         
34523         var filterPattern = function(box, length)
34524         {
34525             if(!box.length){
34526                 return;
34527             }
34528             
34529             var match = false;
34530             
34531             var pattern = box.slice(0, length);
34532             
34533             var format = [];
34534             
34535             Roo.each(pattern, function(i){
34536                 format.push(i.size);
34537             }, this);
34538             
34539             Roo.each(standard, function(s){
34540                 
34541                 if(String(s) != String(format)){
34542                     return;
34543                 }
34544                 
34545                 match = true;
34546                 return false;
34547                 
34548             }, this);
34549             
34550             if(!match && length == 1){
34551                 return;
34552             }
34553             
34554             if(!match){
34555                 filterPattern(box, length - 1);
34556                 return;
34557             }
34558                 
34559             queue.push(pattern);
34560
34561             box = box.slice(length, box.length);
34562
34563             filterPattern(box, 4);
34564
34565             return;
34566             
34567         }
34568         
34569         Roo.each(boxes, function(box, k){
34570             
34571             if(!box.length){
34572                 return;
34573             }
34574             
34575             if(box.length == 1){
34576                 queue.push(box);
34577                 return;
34578             }
34579             
34580             filterPattern(box, 4);
34581             
34582         }, this);
34583         
34584         
34585         var prune = [];
34586         
34587         var pos = this.el.getBox(true);
34588         
34589         var minX = pos.x;
34590         
34591         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34592         
34593         var hit_end = false;
34594         
34595         Roo.each(queue, function(box){
34596             
34597             if(hit_end){
34598                 
34599                 Roo.each(box, function(b){
34600                 
34601                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34602                     b.el.hide();
34603
34604                 }, this);
34605
34606                 return;
34607             }
34608             
34609             var mx = 0;
34610             
34611             Roo.each(box, function(b){
34612                 
34613                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34614                 b.el.show();
34615
34616                 mx = Math.max(mx, b.x);
34617                 
34618             }, this);
34619             
34620             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34621             
34622             if(maxX < minX){
34623                 
34624                 Roo.each(box, function(b){
34625                 
34626                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34627                     b.el.hide();
34628                     
34629                 }, this);
34630                 
34631                 hit_end = true;
34632                 
34633                 return;
34634             }
34635             
34636             prune.push(box);
34637             
34638         }, this);
34639         
34640         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34641     },
34642     
34643     /** Sets position of item in DOM
34644     * @param {Element} item
34645     * @param {Number} x - horizontal position
34646     * @param {Number} y - vertical position
34647     * @param {Boolean} isInstant - disables transitions
34648     */
34649     _processVerticalLayoutQueue : function( queue, isInstant )
34650     {
34651         var pos = this.el.getBox(true);
34652         var x = pos.x;
34653         var y = pos.y;
34654         var maxY = [];
34655         
34656         for (var i = 0; i < this.cols; i++){
34657             maxY[i] = pos.y;
34658         }
34659         
34660         Roo.each(queue, function(box, k){
34661             
34662             var col = k % this.cols;
34663             
34664             Roo.each(box, function(b,kk){
34665                 
34666                 b.el.position('absolute');
34667                 
34668                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34669                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34670                 
34671                 if(b.size == 'md-left' || b.size == 'md-right'){
34672                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34673                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34674                 }
34675                 
34676                 b.el.setWidth(width);
34677                 b.el.setHeight(height);
34678                 // iframe?
34679                 b.el.select('iframe',true).setSize(width,height);
34680                 
34681             }, this);
34682             
34683             for (var i = 0; i < this.cols; i++){
34684                 
34685                 if(maxY[i] < maxY[col]){
34686                     col = i;
34687                     continue;
34688                 }
34689                 
34690                 col = Math.min(col, i);
34691                 
34692             }
34693             
34694             x = pos.x + col * (this.colWidth + this.padWidth);
34695             
34696             y = maxY[col];
34697             
34698             var positions = [];
34699             
34700             switch (box.length){
34701                 case 1 :
34702                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34703                     break;
34704                 case 2 :
34705                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34706                     break;
34707                 case 3 :
34708                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34709                     break;
34710                 case 4 :
34711                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34712                     break;
34713                 default :
34714                     break;
34715             }
34716             
34717             Roo.each(box, function(b,kk){
34718                 
34719                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34720                 
34721                 var sz = b.el.getSize();
34722                 
34723                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34724                 
34725             }, this);
34726             
34727         }, this);
34728         
34729         var mY = 0;
34730         
34731         for (var i = 0; i < this.cols; i++){
34732             mY = Math.max(mY, maxY[i]);
34733         }
34734         
34735         this.el.setHeight(mY - pos.y);
34736         
34737     },
34738     
34739 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34740 //    {
34741 //        var pos = this.el.getBox(true);
34742 //        var x = pos.x;
34743 //        var y = pos.y;
34744 //        var maxX = pos.right;
34745 //        
34746 //        var maxHeight = 0;
34747 //        
34748 //        Roo.each(items, function(item, k){
34749 //            
34750 //            var c = k % 2;
34751 //            
34752 //            item.el.position('absolute');
34753 //                
34754 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34755 //
34756 //            item.el.setWidth(width);
34757 //
34758 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34759 //
34760 //            item.el.setHeight(height);
34761 //            
34762 //            if(c == 0){
34763 //                item.el.setXY([x, y], isInstant ? false : true);
34764 //            } else {
34765 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34766 //            }
34767 //            
34768 //            y = y + height + this.alternativePadWidth;
34769 //            
34770 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34771 //            
34772 //        }, this);
34773 //        
34774 //        this.el.setHeight(maxHeight);
34775 //        
34776 //    },
34777     
34778     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34779     {
34780         var pos = this.el.getBox(true);
34781         
34782         var minX = pos.x;
34783         var minY = pos.y;
34784         
34785         var maxX = pos.right;
34786         
34787         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34788         
34789         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34790         
34791         Roo.each(queue, function(box, k){
34792             
34793             Roo.each(box, function(b, kk){
34794                 
34795                 b.el.position('absolute');
34796                 
34797                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34798                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34799                 
34800                 if(b.size == 'md-left' || b.size == 'md-right'){
34801                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34802                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34803                 }
34804                 
34805                 b.el.setWidth(width);
34806                 b.el.setHeight(height);
34807                 
34808             }, this);
34809             
34810             if(!box.length){
34811                 return;
34812             }
34813             
34814             var positions = [];
34815             
34816             switch (box.length){
34817                 case 1 :
34818                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34819                     break;
34820                 case 2 :
34821                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34822                     break;
34823                 case 3 :
34824                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34825                     break;
34826                 case 4 :
34827                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34828                     break;
34829                 default :
34830                     break;
34831             }
34832             
34833             Roo.each(box, function(b,kk){
34834                 
34835                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34836                 
34837                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34838                 
34839             }, this);
34840             
34841         }, this);
34842         
34843     },
34844     
34845     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34846     {
34847         Roo.each(eItems, function(b,k){
34848             
34849             b.size = (k == 0) ? 'sm' : 'xs';
34850             b.x = (k == 0) ? 2 : 1;
34851             b.y = (k == 0) ? 2 : 1;
34852             
34853             b.el.position('absolute');
34854             
34855             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34856                 
34857             b.el.setWidth(width);
34858             
34859             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34860             
34861             b.el.setHeight(height);
34862             
34863         }, this);
34864
34865         var positions = [];
34866         
34867         positions.push({
34868             x : maxX - this.unitWidth * 2 - this.gutter,
34869             y : minY
34870         });
34871         
34872         positions.push({
34873             x : maxX - this.unitWidth,
34874             y : minY + (this.unitWidth + this.gutter) * 2
34875         });
34876         
34877         positions.push({
34878             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34879             y : minY
34880         });
34881         
34882         Roo.each(eItems, function(b,k){
34883             
34884             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34885
34886         }, this);
34887         
34888     },
34889     
34890     getVerticalOneBoxColPositions : function(x, y, box)
34891     {
34892         var pos = [];
34893         
34894         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34895         
34896         if(box[0].size == 'md-left'){
34897             rand = 0;
34898         }
34899         
34900         if(box[0].size == 'md-right'){
34901             rand = 1;
34902         }
34903         
34904         pos.push({
34905             x : x + (this.unitWidth + this.gutter) * rand,
34906             y : y
34907         });
34908         
34909         return pos;
34910     },
34911     
34912     getVerticalTwoBoxColPositions : function(x, y, box)
34913     {
34914         var pos = [];
34915         
34916         if(box[0].size == 'xs'){
34917             
34918             pos.push({
34919                 x : x,
34920                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34921             });
34922
34923             pos.push({
34924                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34925                 y : y
34926             });
34927             
34928             return pos;
34929             
34930         }
34931         
34932         pos.push({
34933             x : x,
34934             y : y
34935         });
34936
34937         pos.push({
34938             x : x + (this.unitWidth + this.gutter) * 2,
34939             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34940         });
34941         
34942         return pos;
34943         
34944     },
34945     
34946     getVerticalThreeBoxColPositions : function(x, y, box)
34947     {
34948         var pos = [];
34949         
34950         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34951             
34952             pos.push({
34953                 x : x,
34954                 y : y
34955             });
34956
34957             pos.push({
34958                 x : x + (this.unitWidth + this.gutter) * 1,
34959                 y : y
34960             });
34961             
34962             pos.push({
34963                 x : x + (this.unitWidth + this.gutter) * 2,
34964                 y : y
34965             });
34966             
34967             return pos;
34968             
34969         }
34970         
34971         if(box[0].size == 'xs' && box[1].size == 'xs'){
34972             
34973             pos.push({
34974                 x : x,
34975                 y : y
34976             });
34977
34978             pos.push({
34979                 x : x,
34980                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34981             });
34982             
34983             pos.push({
34984                 x : x + (this.unitWidth + this.gutter) * 1,
34985                 y : y
34986             });
34987             
34988             return pos;
34989             
34990         }
34991         
34992         pos.push({
34993             x : x,
34994             y : y
34995         });
34996
34997         pos.push({
34998             x : x + (this.unitWidth + this.gutter) * 2,
34999             y : y
35000         });
35001
35002         pos.push({
35003             x : x + (this.unitWidth + this.gutter) * 2,
35004             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35005         });
35006             
35007         return pos;
35008         
35009     },
35010     
35011     getVerticalFourBoxColPositions : function(x, y, box)
35012     {
35013         var pos = [];
35014         
35015         if(box[0].size == 'xs'){
35016             
35017             pos.push({
35018                 x : x,
35019                 y : y
35020             });
35021
35022             pos.push({
35023                 x : x,
35024                 y : y + (this.unitHeight + this.gutter) * 1
35025             });
35026             
35027             pos.push({
35028                 x : x,
35029                 y : y + (this.unitHeight + this.gutter) * 2
35030             });
35031             
35032             pos.push({
35033                 x : x + (this.unitWidth + this.gutter) * 1,
35034                 y : y
35035             });
35036             
35037             return pos;
35038             
35039         }
35040         
35041         pos.push({
35042             x : x,
35043             y : y
35044         });
35045
35046         pos.push({
35047             x : x + (this.unitWidth + this.gutter) * 2,
35048             y : y
35049         });
35050
35051         pos.push({
35052             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35053             y : y + (this.unitHeight + this.gutter) * 1
35054         });
35055
35056         pos.push({
35057             x : x + (this.unitWidth + this.gutter) * 2,
35058             y : y + (this.unitWidth + this.gutter) * 2
35059         });
35060
35061         return pos;
35062         
35063     },
35064     
35065     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35066     {
35067         var pos = [];
35068         
35069         if(box[0].size == 'md-left'){
35070             pos.push({
35071                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35072                 y : minY
35073             });
35074             
35075             return pos;
35076         }
35077         
35078         if(box[0].size == 'md-right'){
35079             pos.push({
35080                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35081                 y : minY + (this.unitWidth + this.gutter) * 1
35082             });
35083             
35084             return pos;
35085         }
35086         
35087         var rand = Math.floor(Math.random() * (4 - box[0].y));
35088         
35089         pos.push({
35090             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35091             y : minY + (this.unitWidth + this.gutter) * rand
35092         });
35093         
35094         return pos;
35095         
35096     },
35097     
35098     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35099     {
35100         var pos = [];
35101         
35102         if(box[0].size == 'xs'){
35103             
35104             pos.push({
35105                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35106                 y : minY
35107             });
35108
35109             pos.push({
35110                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35111                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35112             });
35113             
35114             return pos;
35115             
35116         }
35117         
35118         pos.push({
35119             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35120             y : minY
35121         });
35122
35123         pos.push({
35124             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35125             y : minY + (this.unitWidth + this.gutter) * 2
35126         });
35127         
35128         return pos;
35129         
35130     },
35131     
35132     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35133     {
35134         var pos = [];
35135         
35136         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35137             
35138             pos.push({
35139                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35140                 y : minY
35141             });
35142
35143             pos.push({
35144                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35145                 y : minY + (this.unitWidth + this.gutter) * 1
35146             });
35147             
35148             pos.push({
35149                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35150                 y : minY + (this.unitWidth + this.gutter) * 2
35151             });
35152             
35153             return pos;
35154             
35155         }
35156         
35157         if(box[0].size == 'xs' && box[1].size == 'xs'){
35158             
35159             pos.push({
35160                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35161                 y : minY
35162             });
35163
35164             pos.push({
35165                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35166                 y : minY
35167             });
35168             
35169             pos.push({
35170                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35171                 y : minY + (this.unitWidth + this.gutter) * 1
35172             });
35173             
35174             return pos;
35175             
35176         }
35177         
35178         pos.push({
35179             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35180             y : minY
35181         });
35182
35183         pos.push({
35184             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35185             y : minY + (this.unitWidth + this.gutter) * 2
35186         });
35187
35188         pos.push({
35189             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35190             y : minY + (this.unitWidth + this.gutter) * 2
35191         });
35192             
35193         return pos;
35194         
35195     },
35196     
35197     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35198     {
35199         var pos = [];
35200         
35201         if(box[0].size == 'xs'){
35202             
35203             pos.push({
35204                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35205                 y : minY
35206             });
35207
35208             pos.push({
35209                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35210                 y : minY
35211             });
35212             
35213             pos.push({
35214                 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),
35215                 y : minY
35216             });
35217             
35218             pos.push({
35219                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35220                 y : minY + (this.unitWidth + this.gutter) * 1
35221             });
35222             
35223             return pos;
35224             
35225         }
35226         
35227         pos.push({
35228             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35229             y : minY
35230         });
35231         
35232         pos.push({
35233             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35234             y : minY + (this.unitWidth + this.gutter) * 2
35235         });
35236         
35237         pos.push({
35238             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35239             y : minY + (this.unitWidth + this.gutter) * 2
35240         });
35241         
35242         pos.push({
35243             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),
35244             y : minY + (this.unitWidth + this.gutter) * 2
35245         });
35246
35247         return pos;
35248         
35249     },
35250     
35251     /**
35252     * remove a Masonry Brick
35253     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35254     */
35255     removeBrick : function(brick_id)
35256     {
35257         if (!brick_id) {
35258             return;
35259         }
35260         
35261         for (var i = 0; i<this.bricks.length; i++) {
35262             if (this.bricks[i].id == brick_id) {
35263                 this.bricks.splice(i,1);
35264                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35265                 this.initial();
35266             }
35267         }
35268     },
35269     
35270     /**
35271     * adds a Masonry Brick
35272     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35273     */
35274     addBrick : function(cfg)
35275     {
35276         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35277         //this.register(cn);
35278         cn.parentId = this.id;
35279         cn.render(this.el);
35280         return cn;
35281     },
35282     
35283     /**
35284     * register a Masonry Brick
35285     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35286     */
35287     
35288     register : function(brick)
35289     {
35290         this.bricks.push(brick);
35291         brick.masonryId = this.id;
35292     },
35293     
35294     /**
35295     * clear all the Masonry Brick
35296     */
35297     clearAll : function()
35298     {
35299         this.bricks = [];
35300         //this.getChildContainer().dom.innerHTML = "";
35301         this.el.dom.innerHTML = '';
35302     },
35303     
35304     getSelected : function()
35305     {
35306         if (!this.selectedBrick) {
35307             return false;
35308         }
35309         
35310         return this.selectedBrick;
35311     }
35312 });
35313
35314 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35315     
35316     groups: {},
35317      /**
35318     * register a Masonry Layout
35319     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35320     */
35321     
35322     register : function(layout)
35323     {
35324         this.groups[layout.id] = layout;
35325     },
35326     /**
35327     * fetch a  Masonry Layout based on the masonry layout ID
35328     * @param {string} the masonry layout to add
35329     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35330     */
35331     
35332     get: function(layout_id) {
35333         if (typeof(this.groups[layout_id]) == 'undefined') {
35334             return false;
35335         }
35336         return this.groups[layout_id] ;
35337     }
35338     
35339     
35340     
35341 });
35342
35343  
35344
35345  /**
35346  *
35347  * This is based on 
35348  * http://masonry.desandro.com
35349  *
35350  * The idea is to render all the bricks based on vertical width...
35351  *
35352  * The original code extends 'outlayer' - we might need to use that....
35353  * 
35354  */
35355
35356
35357 /**
35358  * @class Roo.bootstrap.LayoutMasonryAuto
35359  * @extends Roo.bootstrap.Component
35360  * Bootstrap Layout Masonry class
35361  * 
35362  * @constructor
35363  * Create a new Element
35364  * @param {Object} config The config object
35365  */
35366
35367 Roo.bootstrap.LayoutMasonryAuto = function(config){
35368     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35369 };
35370
35371 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35372     
35373       /**
35374      * @cfg {Boolean} isFitWidth  - resize the width..
35375      */   
35376     isFitWidth : false,  // options..
35377     /**
35378      * @cfg {Boolean} isOriginLeft = left align?
35379      */   
35380     isOriginLeft : true,
35381     /**
35382      * @cfg {Boolean} isOriginTop = top align?
35383      */   
35384     isOriginTop : false,
35385     /**
35386      * @cfg {Boolean} isLayoutInstant = no animation?
35387      */   
35388     isLayoutInstant : false, // needed?
35389     /**
35390      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35391      */   
35392     isResizingContainer : true,
35393     /**
35394      * @cfg {Number} columnWidth  width of the columns 
35395      */   
35396     
35397     columnWidth : 0,
35398     
35399     /**
35400      * @cfg {Number} maxCols maximum number of columns
35401      */   
35402     
35403     maxCols: 0,
35404     /**
35405      * @cfg {Number} padHeight padding below box..
35406      */   
35407     
35408     padHeight : 10, 
35409     
35410     /**
35411      * @cfg {Boolean} isAutoInitial defalut true
35412      */   
35413     
35414     isAutoInitial : true, 
35415     
35416     // private?
35417     gutter : 0,
35418     
35419     containerWidth: 0,
35420     initialColumnWidth : 0,
35421     currentSize : null,
35422     
35423     colYs : null, // array.
35424     maxY : 0,
35425     padWidth: 10,
35426     
35427     
35428     tag: 'div',
35429     cls: '',
35430     bricks: null, //CompositeElement
35431     cols : 0, // array?
35432     // element : null, // wrapped now this.el
35433     _isLayoutInited : null, 
35434     
35435     
35436     getAutoCreate : function(){
35437         
35438         var cfg = {
35439             tag: this.tag,
35440             cls: 'blog-masonary-wrapper ' + this.cls,
35441             cn : {
35442                 cls : 'mas-boxes masonary'
35443             }
35444         };
35445         
35446         return cfg;
35447     },
35448     
35449     getChildContainer: function( )
35450     {
35451         if (this.boxesEl) {
35452             return this.boxesEl;
35453         }
35454         
35455         this.boxesEl = this.el.select('.mas-boxes').first();
35456         
35457         return this.boxesEl;
35458     },
35459     
35460     
35461     initEvents : function()
35462     {
35463         var _this = this;
35464         
35465         if(this.isAutoInitial){
35466             Roo.log('hook children rendered');
35467             this.on('childrenrendered', function() {
35468                 Roo.log('children rendered');
35469                 _this.initial();
35470             } ,this);
35471         }
35472         
35473     },
35474     
35475     initial : function()
35476     {
35477         this.reloadItems();
35478
35479         this.currentSize = this.el.getBox(true);
35480
35481         /// was window resize... - let's see if this works..
35482         Roo.EventManager.onWindowResize(this.resize, this); 
35483
35484         if(!this.isAutoInitial){
35485             this.layout();
35486             return;
35487         }
35488         
35489         this.layout.defer(500,this);
35490     },
35491     
35492     reloadItems: function()
35493     {
35494         this.bricks = this.el.select('.masonry-brick', true);
35495         
35496         this.bricks.each(function(b) {
35497             //Roo.log(b.getSize());
35498             if (!b.attr('originalwidth')) {
35499                 b.attr('originalwidth',  b.getSize().width);
35500             }
35501             
35502         });
35503         
35504         Roo.log(this.bricks.elements.length);
35505     },
35506     
35507     resize : function()
35508     {
35509         Roo.log('resize');
35510         var cs = this.el.getBox(true);
35511         
35512         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35513             Roo.log("no change in with or X");
35514             return;
35515         }
35516         this.currentSize = cs;
35517         this.layout();
35518     },
35519     
35520     layout : function()
35521     {
35522          Roo.log('layout');
35523         this._resetLayout();
35524         //this._manageStamps();
35525       
35526         // don't animate first layout
35527         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35528         this.layoutItems( isInstant );
35529       
35530         // flag for initalized
35531         this._isLayoutInited = true;
35532     },
35533     
35534     layoutItems : function( isInstant )
35535     {
35536         //var items = this._getItemsForLayout( this.items );
35537         // original code supports filtering layout items.. we just ignore it..
35538         
35539         this._layoutItems( this.bricks , isInstant );
35540       
35541         this._postLayout();
35542     },
35543     _layoutItems : function ( items , isInstant)
35544     {
35545        //this.fireEvent( 'layout', this, items );
35546     
35547
35548         if ( !items || !items.elements.length ) {
35549           // no items, emit event with empty array
35550             return;
35551         }
35552
35553         var queue = [];
35554         items.each(function(item) {
35555             Roo.log("layout item");
35556             Roo.log(item);
35557             // get x/y object from method
35558             var position = this._getItemLayoutPosition( item );
35559             // enqueue
35560             position.item = item;
35561             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35562             queue.push( position );
35563         }, this);
35564       
35565         this._processLayoutQueue( queue );
35566     },
35567     /** Sets position of item in DOM
35568     * @param {Element} item
35569     * @param {Number} x - horizontal position
35570     * @param {Number} y - vertical position
35571     * @param {Boolean} isInstant - disables transitions
35572     */
35573     _processLayoutQueue : function( queue )
35574     {
35575         for ( var i=0, len = queue.length; i < len; i++ ) {
35576             var obj = queue[i];
35577             obj.item.position('absolute');
35578             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35579         }
35580     },
35581       
35582     
35583     /**
35584     * Any logic you want to do after each layout,
35585     * i.e. size the container
35586     */
35587     _postLayout : function()
35588     {
35589         this.resizeContainer();
35590     },
35591     
35592     resizeContainer : function()
35593     {
35594         if ( !this.isResizingContainer ) {
35595             return;
35596         }
35597         var size = this._getContainerSize();
35598         if ( size ) {
35599             this.el.setSize(size.width,size.height);
35600             this.boxesEl.setSize(size.width,size.height);
35601         }
35602     },
35603     
35604     
35605     
35606     _resetLayout : function()
35607     {
35608         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35609         this.colWidth = this.el.getWidth();
35610         //this.gutter = this.el.getWidth(); 
35611         
35612         this.measureColumns();
35613
35614         // reset column Y
35615         var i = this.cols;
35616         this.colYs = [];
35617         while (i--) {
35618             this.colYs.push( 0 );
35619         }
35620     
35621         this.maxY = 0;
35622     },
35623
35624     measureColumns : function()
35625     {
35626         this.getContainerWidth();
35627       // if columnWidth is 0, default to outerWidth of first item
35628         if ( !this.columnWidth ) {
35629             var firstItem = this.bricks.first();
35630             Roo.log(firstItem);
35631             this.columnWidth  = this.containerWidth;
35632             if (firstItem && firstItem.attr('originalwidth') ) {
35633                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35634             }
35635             // columnWidth fall back to item of first element
35636             Roo.log("set column width?");
35637                         this.initialColumnWidth = this.columnWidth  ;
35638
35639             // if first elem has no width, default to size of container
35640             
35641         }
35642         
35643         
35644         if (this.initialColumnWidth) {
35645             this.columnWidth = this.initialColumnWidth;
35646         }
35647         
35648         
35649             
35650         // column width is fixed at the top - however if container width get's smaller we should
35651         // reduce it...
35652         
35653         // this bit calcs how man columns..
35654             
35655         var columnWidth = this.columnWidth += this.gutter;
35656       
35657         // calculate columns
35658         var containerWidth = this.containerWidth + this.gutter;
35659         
35660         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35661         // fix rounding errors, typically with gutters
35662         var excess = columnWidth - containerWidth % columnWidth;
35663         
35664         
35665         // if overshoot is less than a pixel, round up, otherwise floor it
35666         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35667         cols = Math[ mathMethod ]( cols );
35668         this.cols = Math.max( cols, 1 );
35669         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35670         
35671          // padding positioning..
35672         var totalColWidth = this.cols * this.columnWidth;
35673         var padavail = this.containerWidth - totalColWidth;
35674         // so for 2 columns - we need 3 'pads'
35675         
35676         var padNeeded = (1+this.cols) * this.padWidth;
35677         
35678         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35679         
35680         this.columnWidth += padExtra
35681         //this.padWidth = Math.floor(padavail /  ( this.cols));
35682         
35683         // adjust colum width so that padding is fixed??
35684         
35685         // we have 3 columns ... total = width * 3
35686         // we have X left over... that should be used by 
35687         
35688         //if (this.expandC) {
35689             
35690         //}
35691         
35692         
35693         
35694     },
35695     
35696     getContainerWidth : function()
35697     {
35698        /* // container is parent if fit width
35699         var container = this.isFitWidth ? this.element.parentNode : this.element;
35700         // check that this.size and size are there
35701         // IE8 triggers resize on body size change, so they might not be
35702         
35703         var size = getSize( container );  //FIXME
35704         this.containerWidth = size && size.innerWidth; //FIXME
35705         */
35706          
35707         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35708         
35709     },
35710     
35711     _getItemLayoutPosition : function( item )  // what is item?
35712     {
35713         // we resize the item to our columnWidth..
35714       
35715         item.setWidth(this.columnWidth);
35716         item.autoBoxAdjust  = false;
35717         
35718         var sz = item.getSize();
35719  
35720         // how many columns does this brick span
35721         var remainder = this.containerWidth % this.columnWidth;
35722         
35723         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35724         // round if off by 1 pixel, otherwise use ceil
35725         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35726         colSpan = Math.min( colSpan, this.cols );
35727         
35728         // normally this should be '1' as we dont' currently allow multi width columns..
35729         
35730         var colGroup = this._getColGroup( colSpan );
35731         // get the minimum Y value from the columns
35732         var minimumY = Math.min.apply( Math, colGroup );
35733         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35734         
35735         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35736          
35737         // position the brick
35738         var position = {
35739             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35740             y: this.currentSize.y + minimumY + this.padHeight
35741         };
35742         
35743         Roo.log(position);
35744         // apply setHeight to necessary columns
35745         var setHeight = minimumY + sz.height + this.padHeight;
35746         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35747         
35748         var setSpan = this.cols + 1 - colGroup.length;
35749         for ( var i = 0; i < setSpan; i++ ) {
35750           this.colYs[ shortColIndex + i ] = setHeight ;
35751         }
35752       
35753         return position;
35754     },
35755     
35756     /**
35757      * @param {Number} colSpan - number of columns the element spans
35758      * @returns {Array} colGroup
35759      */
35760     _getColGroup : function( colSpan )
35761     {
35762         if ( colSpan < 2 ) {
35763           // if brick spans only one column, use all the column Ys
35764           return this.colYs;
35765         }
35766       
35767         var colGroup = [];
35768         // how many different places could this brick fit horizontally
35769         var groupCount = this.cols + 1 - colSpan;
35770         // for each group potential horizontal position
35771         for ( var i = 0; i < groupCount; i++ ) {
35772           // make an array of colY values for that one group
35773           var groupColYs = this.colYs.slice( i, i + colSpan );
35774           // and get the max value of the array
35775           colGroup[i] = Math.max.apply( Math, groupColYs );
35776         }
35777         return colGroup;
35778     },
35779     /*
35780     _manageStamp : function( stamp )
35781     {
35782         var stampSize =  stamp.getSize();
35783         var offset = stamp.getBox();
35784         // get the columns that this stamp affects
35785         var firstX = this.isOriginLeft ? offset.x : offset.right;
35786         var lastX = firstX + stampSize.width;
35787         var firstCol = Math.floor( firstX / this.columnWidth );
35788         firstCol = Math.max( 0, firstCol );
35789         
35790         var lastCol = Math.floor( lastX / this.columnWidth );
35791         // lastCol should not go over if multiple of columnWidth #425
35792         lastCol -= lastX % this.columnWidth ? 0 : 1;
35793         lastCol = Math.min( this.cols - 1, lastCol );
35794         
35795         // set colYs to bottom of the stamp
35796         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35797             stampSize.height;
35798             
35799         for ( var i = firstCol; i <= lastCol; i++ ) {
35800           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35801         }
35802     },
35803     */
35804     
35805     _getContainerSize : function()
35806     {
35807         this.maxY = Math.max.apply( Math, this.colYs );
35808         var size = {
35809             height: this.maxY
35810         };
35811       
35812         if ( this.isFitWidth ) {
35813             size.width = this._getContainerFitWidth();
35814         }
35815       
35816         return size;
35817     },
35818     
35819     _getContainerFitWidth : function()
35820     {
35821         var unusedCols = 0;
35822         // count unused columns
35823         var i = this.cols;
35824         while ( --i ) {
35825           if ( this.colYs[i] !== 0 ) {
35826             break;
35827           }
35828           unusedCols++;
35829         }
35830         // fit container to columns that have been used
35831         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35832     },
35833     
35834     needsResizeLayout : function()
35835     {
35836         var previousWidth = this.containerWidth;
35837         this.getContainerWidth();
35838         return previousWidth !== this.containerWidth;
35839     }
35840  
35841 });
35842
35843  
35844
35845  /*
35846  * - LGPL
35847  *
35848  * element
35849  * 
35850  */
35851
35852 /**
35853  * @class Roo.bootstrap.MasonryBrick
35854  * @extends Roo.bootstrap.Component
35855  * Bootstrap MasonryBrick class
35856  * 
35857  * @constructor
35858  * Create a new MasonryBrick
35859  * @param {Object} config The config object
35860  */
35861
35862 Roo.bootstrap.MasonryBrick = function(config){
35863     
35864     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35865     
35866     Roo.bootstrap.MasonryBrick.register(this);
35867     
35868     this.addEvents({
35869         // raw events
35870         /**
35871          * @event click
35872          * When a MasonryBrick is clcik
35873          * @param {Roo.bootstrap.MasonryBrick} this
35874          * @param {Roo.EventObject} e
35875          */
35876         "click" : true
35877     });
35878 };
35879
35880 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35881     
35882     /**
35883      * @cfg {String} title
35884      */   
35885     title : '',
35886     /**
35887      * @cfg {String} html
35888      */   
35889     html : '',
35890     /**
35891      * @cfg {String} bgimage
35892      */   
35893     bgimage : '',
35894     /**
35895      * @cfg {String} videourl
35896      */   
35897     videourl : '',
35898     /**
35899      * @cfg {String} cls
35900      */   
35901     cls : '',
35902     /**
35903      * @cfg {String} href
35904      */   
35905     href : '',
35906     /**
35907      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35908      */   
35909     size : 'xs',
35910     
35911     /**
35912      * @cfg {String} placetitle (center|bottom)
35913      */   
35914     placetitle : '',
35915     
35916     /**
35917      * @cfg {Boolean} isFitContainer defalut true
35918      */   
35919     isFitContainer : true, 
35920     
35921     /**
35922      * @cfg {Boolean} preventDefault defalut false
35923      */   
35924     preventDefault : false, 
35925     
35926     /**
35927      * @cfg {Boolean} inverse defalut false
35928      */   
35929     maskInverse : false, 
35930     
35931     getAutoCreate : function()
35932     {
35933         if(!this.isFitContainer){
35934             return this.getSplitAutoCreate();
35935         }
35936         
35937         var cls = 'masonry-brick masonry-brick-full';
35938         
35939         if(this.href.length){
35940             cls += ' masonry-brick-link';
35941         }
35942         
35943         if(this.bgimage.length){
35944             cls += ' masonry-brick-image';
35945         }
35946         
35947         if(this.maskInverse){
35948             cls += ' mask-inverse';
35949         }
35950         
35951         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35952             cls += ' enable-mask';
35953         }
35954         
35955         if(this.size){
35956             cls += ' masonry-' + this.size + '-brick';
35957         }
35958         
35959         if(this.placetitle.length){
35960             
35961             switch (this.placetitle) {
35962                 case 'center' :
35963                     cls += ' masonry-center-title';
35964                     break;
35965                 case 'bottom' :
35966                     cls += ' masonry-bottom-title';
35967                     break;
35968                 default:
35969                     break;
35970             }
35971             
35972         } else {
35973             if(!this.html.length && !this.bgimage.length){
35974                 cls += ' masonry-center-title';
35975             }
35976
35977             if(!this.html.length && this.bgimage.length){
35978                 cls += ' masonry-bottom-title';
35979             }
35980         }
35981         
35982         if(this.cls){
35983             cls += ' ' + this.cls;
35984         }
35985         
35986         var cfg = {
35987             tag: (this.href.length) ? 'a' : 'div',
35988             cls: cls,
35989             cn: [
35990                 {
35991                     tag: 'div',
35992                     cls: 'masonry-brick-mask'
35993                 },
35994                 {
35995                     tag: 'div',
35996                     cls: 'masonry-brick-paragraph',
35997                     cn: []
35998                 }
35999             ]
36000         };
36001         
36002         if(this.href.length){
36003             cfg.href = this.href;
36004         }
36005         
36006         var cn = cfg.cn[1].cn;
36007         
36008         if(this.title.length){
36009             cn.push({
36010                 tag: 'h4',
36011                 cls: 'masonry-brick-title',
36012                 html: this.title
36013             });
36014         }
36015         
36016         if(this.html.length){
36017             cn.push({
36018                 tag: 'p',
36019                 cls: 'masonry-brick-text',
36020                 html: this.html
36021             });
36022         }
36023         
36024         if (!this.title.length && !this.html.length) {
36025             cfg.cn[1].cls += ' hide';
36026         }
36027         
36028         if(this.bgimage.length){
36029             cfg.cn.push({
36030                 tag: 'img',
36031                 cls: 'masonry-brick-image-view',
36032                 src: this.bgimage
36033             });
36034         }
36035         
36036         if(this.videourl.length){
36037             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36038             // youtube support only?
36039             cfg.cn.push({
36040                 tag: 'iframe',
36041                 cls: 'masonry-brick-image-view',
36042                 src: vurl,
36043                 frameborder : 0,
36044                 allowfullscreen : true
36045             });
36046         }
36047         
36048         return cfg;
36049         
36050     },
36051     
36052     getSplitAutoCreate : function()
36053     {
36054         var cls = 'masonry-brick masonry-brick-split';
36055         
36056         if(this.href.length){
36057             cls += ' masonry-brick-link';
36058         }
36059         
36060         if(this.bgimage.length){
36061             cls += ' masonry-brick-image';
36062         }
36063         
36064         if(this.size){
36065             cls += ' masonry-' + this.size + '-brick';
36066         }
36067         
36068         switch (this.placetitle) {
36069             case 'center' :
36070                 cls += ' masonry-center-title';
36071                 break;
36072             case 'bottom' :
36073                 cls += ' masonry-bottom-title';
36074                 break;
36075             default:
36076                 if(!this.bgimage.length){
36077                     cls += ' masonry-center-title';
36078                 }
36079
36080                 if(this.bgimage.length){
36081                     cls += ' masonry-bottom-title';
36082                 }
36083                 break;
36084         }
36085         
36086         if(this.cls){
36087             cls += ' ' + this.cls;
36088         }
36089         
36090         var cfg = {
36091             tag: (this.href.length) ? 'a' : 'div',
36092             cls: cls,
36093             cn: [
36094                 {
36095                     tag: 'div',
36096                     cls: 'masonry-brick-split-head',
36097                     cn: [
36098                         {
36099                             tag: 'div',
36100                             cls: 'masonry-brick-paragraph',
36101                             cn: []
36102                         }
36103                     ]
36104                 },
36105                 {
36106                     tag: 'div',
36107                     cls: 'masonry-brick-split-body',
36108                     cn: []
36109                 }
36110             ]
36111         };
36112         
36113         if(this.href.length){
36114             cfg.href = this.href;
36115         }
36116         
36117         if(this.title.length){
36118             cfg.cn[0].cn[0].cn.push({
36119                 tag: 'h4',
36120                 cls: 'masonry-brick-title',
36121                 html: this.title
36122             });
36123         }
36124         
36125         if(this.html.length){
36126             cfg.cn[1].cn.push({
36127                 tag: 'p',
36128                 cls: 'masonry-brick-text',
36129                 html: this.html
36130             });
36131         }
36132
36133         if(this.bgimage.length){
36134             cfg.cn[0].cn.push({
36135                 tag: 'img',
36136                 cls: 'masonry-brick-image-view',
36137                 src: this.bgimage
36138             });
36139         }
36140         
36141         if(this.videourl.length){
36142             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36143             // youtube support only?
36144             cfg.cn[0].cn.cn.push({
36145                 tag: 'iframe',
36146                 cls: 'masonry-brick-image-view',
36147                 src: vurl,
36148                 frameborder : 0,
36149                 allowfullscreen : true
36150             });
36151         }
36152         
36153         return cfg;
36154     },
36155     
36156     initEvents: function() 
36157     {
36158         switch (this.size) {
36159             case 'xs' :
36160                 this.x = 1;
36161                 this.y = 1;
36162                 break;
36163             case 'sm' :
36164                 this.x = 2;
36165                 this.y = 2;
36166                 break;
36167             case 'md' :
36168             case 'md-left' :
36169             case 'md-right' :
36170                 this.x = 3;
36171                 this.y = 3;
36172                 break;
36173             case 'tall' :
36174                 this.x = 2;
36175                 this.y = 3;
36176                 break;
36177             case 'wide' :
36178                 this.x = 3;
36179                 this.y = 2;
36180                 break;
36181             case 'wide-thin' :
36182                 this.x = 3;
36183                 this.y = 1;
36184                 break;
36185                         
36186             default :
36187                 break;
36188         }
36189         
36190         if(Roo.isTouch){
36191             this.el.on('touchstart', this.onTouchStart, this);
36192             this.el.on('touchmove', this.onTouchMove, this);
36193             this.el.on('touchend', this.onTouchEnd, this);
36194             this.el.on('contextmenu', this.onContextMenu, this);
36195         } else {
36196             this.el.on('mouseenter'  ,this.enter, this);
36197             this.el.on('mouseleave', this.leave, this);
36198             this.el.on('click', this.onClick, this);
36199         }
36200         
36201         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36202             this.parent().bricks.push(this);   
36203         }
36204         
36205     },
36206     
36207     onClick: function(e, el)
36208     {
36209         var time = this.endTimer - this.startTimer;
36210         // Roo.log(e.preventDefault());
36211         if(Roo.isTouch){
36212             if(time > 1000){
36213                 e.preventDefault();
36214                 return;
36215             }
36216         }
36217         
36218         if(!this.preventDefault){
36219             return;
36220         }
36221         
36222         e.preventDefault();
36223         
36224         if (this.activeClass != '') {
36225             this.selectBrick();
36226         }
36227         
36228         this.fireEvent('click', this, e);
36229     },
36230     
36231     enter: function(e, el)
36232     {
36233         e.preventDefault();
36234         
36235         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36236             return;
36237         }
36238         
36239         if(this.bgimage.length && this.html.length){
36240             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36241         }
36242     },
36243     
36244     leave: function(e, el)
36245     {
36246         e.preventDefault();
36247         
36248         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36249             return;
36250         }
36251         
36252         if(this.bgimage.length && this.html.length){
36253             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36254         }
36255     },
36256     
36257     onTouchStart: function(e, el)
36258     {
36259 //        e.preventDefault();
36260         
36261         this.touchmoved = false;
36262         
36263         if(!this.isFitContainer){
36264             return;
36265         }
36266         
36267         if(!this.bgimage.length || !this.html.length){
36268             return;
36269         }
36270         
36271         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36272         
36273         this.timer = new Date().getTime();
36274         
36275     },
36276     
36277     onTouchMove: function(e, el)
36278     {
36279         this.touchmoved = true;
36280     },
36281     
36282     onContextMenu : function(e,el)
36283     {
36284         e.preventDefault();
36285         e.stopPropagation();
36286         return false;
36287     },
36288     
36289     onTouchEnd: function(e, el)
36290     {
36291 //        e.preventDefault();
36292         
36293         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36294         
36295             this.leave(e,el);
36296             
36297             return;
36298         }
36299         
36300         if(!this.bgimage.length || !this.html.length){
36301             
36302             if(this.href.length){
36303                 window.location.href = this.href;
36304             }
36305             
36306             return;
36307         }
36308         
36309         if(!this.isFitContainer){
36310             return;
36311         }
36312         
36313         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36314         
36315         window.location.href = this.href;
36316     },
36317     
36318     //selection on single brick only
36319     selectBrick : function() {
36320         
36321         if (!this.parentId) {
36322             return;
36323         }
36324         
36325         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36326         var index = m.selectedBrick.indexOf(this.id);
36327         
36328         if ( index > -1) {
36329             m.selectedBrick.splice(index,1);
36330             this.el.removeClass(this.activeClass);
36331             return;
36332         }
36333         
36334         for(var i = 0; i < m.selectedBrick.length; i++) {
36335             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36336             b.el.removeClass(b.activeClass);
36337         }
36338         
36339         m.selectedBrick = [];
36340         
36341         m.selectedBrick.push(this.id);
36342         this.el.addClass(this.activeClass);
36343         return;
36344     },
36345     
36346     isSelected : function(){
36347         return this.el.hasClass(this.activeClass);
36348         
36349     }
36350 });
36351
36352 Roo.apply(Roo.bootstrap.MasonryBrick, {
36353     
36354     //groups: {},
36355     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36356      /**
36357     * register a Masonry Brick
36358     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36359     */
36360     
36361     register : function(brick)
36362     {
36363         //this.groups[brick.id] = brick;
36364         this.groups.add(brick.id, brick);
36365     },
36366     /**
36367     * fetch a  masonry brick based on the masonry brick ID
36368     * @param {string} the masonry brick to add
36369     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36370     */
36371     
36372     get: function(brick_id) 
36373     {
36374         // if (typeof(this.groups[brick_id]) == 'undefined') {
36375         //     return false;
36376         // }
36377         // return this.groups[brick_id] ;
36378         
36379         if(this.groups.key(brick_id)) {
36380             return this.groups.key(brick_id);
36381         }
36382         
36383         return false;
36384     }
36385     
36386     
36387     
36388 });
36389
36390  /*
36391  * - LGPL
36392  *
36393  * element
36394  * 
36395  */
36396
36397 /**
36398  * @class Roo.bootstrap.Brick
36399  * @extends Roo.bootstrap.Component
36400  * Bootstrap Brick class
36401  * 
36402  * @constructor
36403  * Create a new Brick
36404  * @param {Object} config The config object
36405  */
36406
36407 Roo.bootstrap.Brick = function(config){
36408     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36409     
36410     this.addEvents({
36411         // raw events
36412         /**
36413          * @event click
36414          * When a Brick is click
36415          * @param {Roo.bootstrap.Brick} this
36416          * @param {Roo.EventObject} e
36417          */
36418         "click" : true
36419     });
36420 };
36421
36422 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36423     
36424     /**
36425      * @cfg {String} title
36426      */   
36427     title : '',
36428     /**
36429      * @cfg {String} html
36430      */   
36431     html : '',
36432     /**
36433      * @cfg {String} bgimage
36434      */   
36435     bgimage : '',
36436     /**
36437      * @cfg {String} cls
36438      */   
36439     cls : '',
36440     /**
36441      * @cfg {String} href
36442      */   
36443     href : '',
36444     /**
36445      * @cfg {String} video
36446      */   
36447     video : '',
36448     /**
36449      * @cfg {Boolean} square
36450      */   
36451     square : true,
36452     
36453     getAutoCreate : function()
36454     {
36455         var cls = 'roo-brick';
36456         
36457         if(this.href.length){
36458             cls += ' roo-brick-link';
36459         }
36460         
36461         if(this.bgimage.length){
36462             cls += ' roo-brick-image';
36463         }
36464         
36465         if(!this.html.length && !this.bgimage.length){
36466             cls += ' roo-brick-center-title';
36467         }
36468         
36469         if(!this.html.length && this.bgimage.length){
36470             cls += ' roo-brick-bottom-title';
36471         }
36472         
36473         if(this.cls){
36474             cls += ' ' + this.cls;
36475         }
36476         
36477         var cfg = {
36478             tag: (this.href.length) ? 'a' : 'div',
36479             cls: cls,
36480             cn: [
36481                 {
36482                     tag: 'div',
36483                     cls: 'roo-brick-paragraph',
36484                     cn: []
36485                 }
36486             ]
36487         };
36488         
36489         if(this.href.length){
36490             cfg.href = this.href;
36491         }
36492         
36493         var cn = cfg.cn[0].cn;
36494         
36495         if(this.title.length){
36496             cn.push({
36497                 tag: 'h4',
36498                 cls: 'roo-brick-title',
36499                 html: this.title
36500             });
36501         }
36502         
36503         if(this.html.length){
36504             cn.push({
36505                 tag: 'p',
36506                 cls: 'roo-brick-text',
36507                 html: this.html
36508             });
36509         } else {
36510             cn.cls += ' hide';
36511         }
36512         
36513         if(this.bgimage.length){
36514             cfg.cn.push({
36515                 tag: 'img',
36516                 cls: 'roo-brick-image-view',
36517                 src: this.bgimage
36518             });
36519         }
36520         
36521         return cfg;
36522     },
36523     
36524     initEvents: function() 
36525     {
36526         if(this.title.length || this.html.length){
36527             this.el.on('mouseenter'  ,this.enter, this);
36528             this.el.on('mouseleave', this.leave, this);
36529         }
36530         
36531         Roo.EventManager.onWindowResize(this.resize, this); 
36532         
36533         if(this.bgimage.length){
36534             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36535             this.imageEl.on('load', this.onImageLoad, this);
36536             return;
36537         }
36538         
36539         this.resize();
36540     },
36541     
36542     onImageLoad : function()
36543     {
36544         this.resize();
36545     },
36546     
36547     resize : function()
36548     {
36549         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36550         
36551         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36552         
36553         if(this.bgimage.length){
36554             var image = this.el.select('.roo-brick-image-view', true).first();
36555             
36556             image.setWidth(paragraph.getWidth());
36557             
36558             if(this.square){
36559                 image.setHeight(paragraph.getWidth());
36560             }
36561             
36562             this.el.setHeight(image.getHeight());
36563             paragraph.setHeight(image.getHeight());
36564             
36565         }
36566         
36567     },
36568     
36569     enter: function(e, el)
36570     {
36571         e.preventDefault();
36572         
36573         if(this.bgimage.length){
36574             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36575             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36576         }
36577     },
36578     
36579     leave: function(e, el)
36580     {
36581         e.preventDefault();
36582         
36583         if(this.bgimage.length){
36584             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36585             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36586         }
36587     }
36588     
36589 });
36590
36591  
36592
36593  /*
36594  * - LGPL
36595  *
36596  * Number field 
36597  */
36598
36599 /**
36600  * @class Roo.bootstrap.form.NumberField
36601  * @extends Roo.bootstrap.form.Input
36602  * Bootstrap NumberField class
36603  * 
36604  * 
36605  * 
36606  * 
36607  * @constructor
36608  * Create a new NumberField
36609  * @param {Object} config The config object
36610  */
36611
36612 Roo.bootstrap.form.NumberField = function(config){
36613     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36614 };
36615
36616 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36617     
36618     /**
36619      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36620      */
36621     allowDecimals : true,
36622     /**
36623      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36624      */
36625     decimalSeparator : ".",
36626     /**
36627      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36628      */
36629     decimalPrecision : 2,
36630     /**
36631      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36632      */
36633     allowNegative : true,
36634     
36635     /**
36636      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36637      */
36638     allowZero: true,
36639     /**
36640      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36641      */
36642     minValue : Number.NEGATIVE_INFINITY,
36643     /**
36644      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36645      */
36646     maxValue : Number.MAX_VALUE,
36647     /**
36648      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36649      */
36650     minText : "The minimum value for this field is {0}",
36651     /**
36652      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36653      */
36654     maxText : "The maximum value for this field is {0}",
36655     /**
36656      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36657      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36658      */
36659     nanText : "{0} is not a valid number",
36660     /**
36661      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36662      */
36663     thousandsDelimiter : false,
36664     /**
36665      * @cfg {String} valueAlign alignment of value
36666      */
36667     valueAlign : "left",
36668
36669     getAutoCreate : function()
36670     {
36671         var hiddenInput = {
36672             tag: 'input',
36673             type: 'hidden',
36674             id: Roo.id(),
36675             cls: 'hidden-number-input'
36676         };
36677         
36678         if (this.name) {
36679             hiddenInput.name = this.name;
36680         }
36681         
36682         this.name = '';
36683         
36684         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36685         
36686         this.name = hiddenInput.name;
36687         
36688         if(cfg.cn.length > 0) {
36689             cfg.cn.push(hiddenInput);
36690         }
36691         
36692         return cfg;
36693     },
36694
36695     // private
36696     initEvents : function()
36697     {   
36698         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36699         
36700         var allowed = "0123456789";
36701         
36702         if(this.allowDecimals){
36703             allowed += this.decimalSeparator;
36704         }
36705         
36706         if(this.allowNegative){
36707             allowed += "-";
36708         }
36709         
36710         if(this.thousandsDelimiter) {
36711             allowed += ",";
36712         }
36713         
36714         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36715         
36716         var keyPress = function(e){
36717             
36718             var k = e.getKey();
36719             
36720             var c = e.getCharCode();
36721             
36722             if(
36723                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36724                     allowed.indexOf(String.fromCharCode(c)) === -1
36725             ){
36726                 e.stopEvent();
36727                 return;
36728             }
36729             
36730             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36731                 return;
36732             }
36733             
36734             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36735                 e.stopEvent();
36736             }
36737         };
36738         
36739         this.el.on("keypress", keyPress, this);
36740     },
36741     
36742     validateValue : function(value)
36743     {
36744         
36745         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36746             return false;
36747         }
36748         
36749         var num = this.parseValue(value);
36750         
36751         if(isNaN(num)){
36752             this.markInvalid(String.format(this.nanText, value));
36753             return false;
36754         }
36755         
36756         if(num < this.minValue){
36757             this.markInvalid(String.format(this.minText, this.minValue));
36758             return false;
36759         }
36760         
36761         if(num > this.maxValue){
36762             this.markInvalid(String.format(this.maxText, this.maxValue));
36763             return false;
36764         }
36765         
36766         return true;
36767     },
36768
36769     getValue : function()
36770     {
36771         var v = this.hiddenEl().getValue();
36772         
36773         return this.fixPrecision(this.parseValue(v));
36774     },
36775
36776     parseValue : function(value)
36777     {
36778         if(this.thousandsDelimiter) {
36779             value += "";
36780             r = new RegExp(",", "g");
36781             value = value.replace(r, "");
36782         }
36783         
36784         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36785         return isNaN(value) ? '' : value;
36786     },
36787
36788     fixPrecision : function(value)
36789     {
36790         if(this.thousandsDelimiter) {
36791             value += "";
36792             r = new RegExp(",", "g");
36793             value = value.replace(r, "");
36794         }
36795         
36796         var nan = isNaN(value);
36797         
36798         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36799             return nan ? '' : value;
36800         }
36801         return parseFloat(value).toFixed(this.decimalPrecision);
36802     },
36803
36804     setValue : function(v)
36805     {
36806         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36807         
36808         this.value = v;
36809         
36810         if(this.rendered){
36811             
36812             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36813             
36814             this.inputEl().dom.value = (v == '') ? '' :
36815                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36816             
36817             if(!this.allowZero && v === '0') {
36818                 this.hiddenEl().dom.value = '';
36819                 this.inputEl().dom.value = '';
36820             }
36821             
36822             this.validate();
36823         }
36824     },
36825
36826     decimalPrecisionFcn : function(v)
36827     {
36828         return Math.floor(v);
36829     },
36830
36831     beforeBlur : function()
36832     {
36833         var v = this.parseValue(this.getRawValue());
36834         
36835         if(v || v === 0 || v === ''){
36836             this.setValue(v);
36837         }
36838     },
36839     
36840     hiddenEl : function()
36841     {
36842         return this.el.select('input.hidden-number-input',true).first();
36843     }
36844     
36845 });
36846
36847  
36848
36849 /*
36850 * Licence: LGPL
36851 */
36852
36853 /**
36854  * @class Roo.bootstrap.DocumentSlider
36855  * @extends Roo.bootstrap.Component
36856  * Bootstrap DocumentSlider class
36857  * 
36858  * @constructor
36859  * Create a new DocumentViewer
36860  * @param {Object} config The config object
36861  */
36862
36863 Roo.bootstrap.DocumentSlider = function(config){
36864     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36865     
36866     this.files = [];
36867     
36868     this.addEvents({
36869         /**
36870          * @event initial
36871          * Fire after initEvent
36872          * @param {Roo.bootstrap.DocumentSlider} this
36873          */
36874         "initial" : true,
36875         /**
36876          * @event update
36877          * Fire after update
36878          * @param {Roo.bootstrap.DocumentSlider} this
36879          */
36880         "update" : true,
36881         /**
36882          * @event click
36883          * Fire after click
36884          * @param {Roo.bootstrap.DocumentSlider} this
36885          */
36886         "click" : true
36887     });
36888 };
36889
36890 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36891     
36892     files : false,
36893     
36894     indicator : 0,
36895     
36896     getAutoCreate : function()
36897     {
36898         var cfg = {
36899             tag : 'div',
36900             cls : 'roo-document-slider',
36901             cn : [
36902                 {
36903                     tag : 'div',
36904                     cls : 'roo-document-slider-header',
36905                     cn : [
36906                         {
36907                             tag : 'div',
36908                             cls : 'roo-document-slider-header-title'
36909                         }
36910                     ]
36911                 },
36912                 {
36913                     tag : 'div',
36914                     cls : 'roo-document-slider-body',
36915                     cn : [
36916                         {
36917                             tag : 'div',
36918                             cls : 'roo-document-slider-prev',
36919                             cn : [
36920                                 {
36921                                     tag : 'i',
36922                                     cls : 'fa fa-chevron-left'
36923                                 }
36924                             ]
36925                         },
36926                         {
36927                             tag : 'div',
36928                             cls : 'roo-document-slider-thumb',
36929                             cn : [
36930                                 {
36931                                     tag : 'img',
36932                                     cls : 'roo-document-slider-image'
36933                                 }
36934                             ]
36935                         },
36936                         {
36937                             tag : 'div',
36938                             cls : 'roo-document-slider-next',
36939                             cn : [
36940                                 {
36941                                     tag : 'i',
36942                                     cls : 'fa fa-chevron-right'
36943                                 }
36944                             ]
36945                         }
36946                     ]
36947                 }
36948             ]
36949         };
36950         
36951         return cfg;
36952     },
36953     
36954     initEvents : function()
36955     {
36956         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36957         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36958         
36959         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36960         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36961         
36962         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36963         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36964         
36965         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36966         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36967         
36968         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36969         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36970         
36971         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36972         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36973         
36974         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36975         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36976         
36977         this.thumbEl.on('click', this.onClick, this);
36978         
36979         this.prevIndicator.on('click', this.prev, this);
36980         
36981         this.nextIndicator.on('click', this.next, this);
36982         
36983     },
36984     
36985     initial : function()
36986     {
36987         if(this.files.length){
36988             this.indicator = 1;
36989             this.update()
36990         }
36991         
36992         this.fireEvent('initial', this);
36993     },
36994     
36995     update : function()
36996     {
36997         this.imageEl.attr('src', this.files[this.indicator - 1]);
36998         
36999         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37000         
37001         this.prevIndicator.show();
37002         
37003         if(this.indicator == 1){
37004             this.prevIndicator.hide();
37005         }
37006         
37007         this.nextIndicator.show();
37008         
37009         if(this.indicator == this.files.length){
37010             this.nextIndicator.hide();
37011         }
37012         
37013         this.thumbEl.scrollTo('top');
37014         
37015         this.fireEvent('update', this);
37016     },
37017     
37018     onClick : function(e)
37019     {
37020         e.preventDefault();
37021         
37022         this.fireEvent('click', this);
37023     },
37024     
37025     prev : function(e)
37026     {
37027         e.preventDefault();
37028         
37029         this.indicator = Math.max(1, this.indicator - 1);
37030         
37031         this.update();
37032     },
37033     
37034     next : function(e)
37035     {
37036         e.preventDefault();
37037         
37038         this.indicator = Math.min(this.files.length, this.indicator + 1);
37039         
37040         this.update();
37041     }
37042 });
37043 /*
37044  * - LGPL
37045  *
37046  * RadioSet
37047  *
37048  *
37049  */
37050
37051 /**
37052  * @class Roo.bootstrap.form.RadioSet
37053  * @extends Roo.bootstrap.form.Input
37054  * @children Roo.bootstrap.form.Radio
37055  * Bootstrap RadioSet class
37056  * @cfg {String} indicatorpos (left|right) default left
37057  * @cfg {Boolean} inline (true|false) inline the element (default true)
37058  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37059  * @constructor
37060  * Create a new RadioSet
37061  * @param {Object} config The config object
37062  */
37063
37064 Roo.bootstrap.form.RadioSet = function(config){
37065     
37066     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37067     
37068     this.radioes = [];
37069     
37070     Roo.bootstrap.form.RadioSet.register(this);
37071     
37072     this.addEvents({
37073         /**
37074         * @event check
37075         * Fires when the element is checked or unchecked.
37076         * @param {Roo.bootstrap.form.RadioSet} this This radio
37077         * @param {Roo.bootstrap.form.Radio} item The checked item
37078         */
37079        check : true,
37080        /**
37081         * @event click
37082         * Fires when the element is click.
37083         * @param {Roo.bootstrap.form.RadioSet} this This radio set
37084         * @param {Roo.bootstrap.form.Radio} item The checked item
37085         * @param {Roo.EventObject} e The event object
37086         */
37087        click : true
37088     });
37089     
37090 };
37091
37092 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
37093
37094     radioes : false,
37095     
37096     inline : true,
37097     
37098     weight : '',
37099     
37100     indicatorpos : 'left',
37101     
37102     getAutoCreate : function()
37103     {
37104         var label = {
37105             tag : 'label',
37106             cls : 'roo-radio-set-label',
37107             cn : [
37108                 {
37109                     tag : 'span',
37110                     html : this.fieldLabel
37111                 }
37112             ]
37113         };
37114         if (Roo.bootstrap.version == 3) {
37115             
37116             
37117             if(this.indicatorpos == 'left'){
37118                 label.cn.unshift({
37119                     tag : 'i',
37120                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37121                     tooltip : 'This field is required'
37122                 });
37123             } else {
37124                 label.cn.push({
37125                     tag : 'i',
37126                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37127                     tooltip : 'This field is required'
37128                 });
37129             }
37130         }
37131         var items = {
37132             tag : 'div',
37133             cls : 'roo-radio-set-items'
37134         };
37135         
37136         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37137         
37138         if (align === 'left' && this.fieldLabel.length) {
37139             
37140             items = {
37141                 cls : "roo-radio-set-right", 
37142                 cn: [
37143                     items
37144                 ]
37145             };
37146             
37147             if(this.labelWidth > 12){
37148                 label.style = "width: " + this.labelWidth + 'px';
37149             }
37150             
37151             if(this.labelWidth < 13 && this.labelmd == 0){
37152                 this.labelmd = this.labelWidth;
37153             }
37154             
37155             if(this.labellg > 0){
37156                 label.cls += ' col-lg-' + this.labellg;
37157                 items.cls += ' col-lg-' + (12 - this.labellg);
37158             }
37159             
37160             if(this.labelmd > 0){
37161                 label.cls += ' col-md-' + this.labelmd;
37162                 items.cls += ' col-md-' + (12 - this.labelmd);
37163             }
37164             
37165             if(this.labelsm > 0){
37166                 label.cls += ' col-sm-' + this.labelsm;
37167                 items.cls += ' col-sm-' + (12 - this.labelsm);
37168             }
37169             
37170             if(this.labelxs > 0){
37171                 label.cls += ' col-xs-' + this.labelxs;
37172                 items.cls += ' col-xs-' + (12 - this.labelxs);
37173             }
37174         }
37175         
37176         var cfg = {
37177             tag : 'div',
37178             cls : 'roo-radio-set',
37179             cn : [
37180                 {
37181                     tag : 'input',
37182                     cls : 'roo-radio-set-input',
37183                     type : 'hidden',
37184                     name : this.name,
37185                     value : this.value ? this.value :  ''
37186                 },
37187                 label,
37188                 items
37189             ]
37190         };
37191         
37192         if(this.weight.length){
37193             cfg.cls += ' roo-radio-' + this.weight;
37194         }
37195         
37196         if(this.inline) {
37197             cfg.cls += ' roo-radio-set-inline';
37198         }
37199         
37200         var settings=this;
37201         ['xs','sm','md','lg'].map(function(size){
37202             if (settings[size]) {
37203                 cfg.cls += ' col-' + size + '-' + settings[size];
37204             }
37205         });
37206         
37207         return cfg;
37208         
37209     },
37210
37211     initEvents : function()
37212     {
37213         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37214         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37215         
37216         if(!this.fieldLabel.length){
37217             this.labelEl.hide();
37218         }
37219         
37220         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37221         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37222         
37223         this.indicator = this.indicatorEl();
37224         
37225         if(this.indicator){
37226             this.indicator.addClass('invisible');
37227         }
37228         
37229         this.originalValue = this.getValue();
37230         
37231     },
37232     
37233     inputEl: function ()
37234     {
37235         return this.el.select('.roo-radio-set-input', true).first();
37236     },
37237     
37238     getChildContainer : function()
37239     {
37240         return this.itemsEl;
37241     },
37242     
37243     register : function(item)
37244     {
37245         this.radioes.push(item);
37246         
37247     },
37248     
37249     validate : function()
37250     {   
37251         if(this.getVisibilityEl().hasClass('hidden')){
37252             return true;
37253         }
37254         
37255         var valid = false;
37256         
37257         Roo.each(this.radioes, function(i){
37258             if(!i.checked){
37259                 return;
37260             }
37261             
37262             valid = true;
37263             return false;
37264         });
37265         
37266         if(this.allowBlank) {
37267             return true;
37268         }
37269         
37270         if(this.disabled || valid){
37271             this.markValid();
37272             return true;
37273         }
37274         
37275         this.markInvalid();
37276         return false;
37277         
37278     },
37279     
37280     markValid : function()
37281     {
37282         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37283             this.indicatorEl().removeClass('visible');
37284             this.indicatorEl().addClass('invisible');
37285         }
37286         
37287         
37288         if (Roo.bootstrap.version == 3) {
37289             this.el.removeClass([this.invalidClass, this.validClass]);
37290             this.el.addClass(this.validClass);
37291         } else {
37292             this.el.removeClass(['is-invalid','is-valid']);
37293             this.el.addClass(['is-valid']);
37294         }
37295         this.fireEvent('valid', this);
37296     },
37297     
37298     markInvalid : function(msg)
37299     {
37300         if(this.allowBlank || this.disabled){
37301             return;
37302         }
37303         
37304         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37305             this.indicatorEl().removeClass('invisible');
37306             this.indicatorEl().addClass('visible');
37307         }
37308         if (Roo.bootstrap.version == 3) {
37309             this.el.removeClass([this.invalidClass, this.validClass]);
37310             this.el.addClass(this.invalidClass);
37311         } else {
37312             this.el.removeClass(['is-invalid','is-valid']);
37313             this.el.addClass(['is-invalid']);
37314         }
37315         
37316         this.fireEvent('invalid', this, msg);
37317         
37318     },
37319     
37320     setValue : function(v, suppressEvent)
37321     {   
37322         if(this.value === v){
37323             return;
37324         }
37325         
37326         this.value = v;
37327         
37328         if(this.rendered){
37329             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37330         }
37331         
37332         Roo.each(this.radioes, function(i){
37333             i.checked = false;
37334             i.el.removeClass('checked');
37335         });
37336         
37337         Roo.each(this.radioes, function(i){
37338             
37339             if(i.value === v || i.value.toString() === v.toString()){
37340                 i.checked = true;
37341                 i.el.addClass('checked');
37342                 
37343                 if(suppressEvent !== true){
37344                     this.fireEvent('check', this, i);
37345                 }
37346                 
37347                 return false;
37348             }
37349             
37350         }, this);
37351         
37352         this.validate();
37353     },
37354     
37355     clearInvalid : function(){
37356         
37357         if(!this.el || this.preventMark){
37358             return;
37359         }
37360         
37361         this.el.removeClass([this.invalidClass]);
37362         
37363         this.fireEvent('valid', this);
37364     }
37365     
37366 });
37367
37368 Roo.apply(Roo.bootstrap.form.RadioSet, {
37369     
37370     groups: {},
37371     
37372     register : function(set)
37373     {
37374         this.groups[set.name] = set;
37375     },
37376     
37377     get: function(name) 
37378     {
37379         if (typeof(this.groups[name]) == 'undefined') {
37380             return false;
37381         }
37382         
37383         return this.groups[name] ;
37384     }
37385     
37386 });
37387 /*
37388  * Based on:
37389  * Ext JS Library 1.1.1
37390  * Copyright(c) 2006-2007, Ext JS, LLC.
37391  *
37392  * Originally Released Under LGPL - original licence link has changed is not relivant.
37393  *
37394  * Fork - LGPL
37395  * <script type="text/javascript">
37396  */
37397
37398
37399 /**
37400  * @class Roo.bootstrap.SplitBar
37401  * @extends Roo.util.Observable
37402  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37403  * <br><br>
37404  * Usage:
37405  * <pre><code>
37406 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37407                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37408 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37409 split.minSize = 100;
37410 split.maxSize = 600;
37411 split.animate = true;
37412 split.on('moved', splitterMoved);
37413 </code></pre>
37414  * @constructor
37415  * Create a new SplitBar
37416  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37417  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37418  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37419  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37420                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37421                         position of the SplitBar).
37422  */
37423 Roo.bootstrap.SplitBar = function(cfg){
37424     
37425     /** @private */
37426     
37427     //{
37428     //  dragElement : elm
37429     //  resizingElement: el,
37430         // optional..
37431     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37432     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37433         // existingProxy ???
37434     //}
37435     
37436     this.el = Roo.get(cfg.dragElement, true);
37437     this.el.dom.unselectable = "on";
37438     /** @private */
37439     this.resizingEl = Roo.get(cfg.resizingElement, true);
37440
37441     /**
37442      * @private
37443      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37444      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37445      * @type Number
37446      */
37447     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37448     
37449     /**
37450      * The minimum size of the resizing element. (Defaults to 0)
37451      * @type Number
37452      */
37453     this.minSize = 0;
37454     
37455     /**
37456      * The maximum size of the resizing element. (Defaults to 2000)
37457      * @type Number
37458      */
37459     this.maxSize = 2000;
37460     
37461     /**
37462      * Whether to animate the transition to the new size
37463      * @type Boolean
37464      */
37465     this.animate = false;
37466     
37467     /**
37468      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37469      * @type Boolean
37470      */
37471     this.useShim = false;
37472     
37473     /** @private */
37474     this.shim = null;
37475     
37476     if(!cfg.existingProxy){
37477         /** @private */
37478         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37479     }else{
37480         this.proxy = Roo.get(cfg.existingProxy).dom;
37481     }
37482     /** @private */
37483     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37484     
37485     /** @private */
37486     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37487     
37488     /** @private */
37489     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37490     
37491     /** @private */
37492     this.dragSpecs = {};
37493     
37494     /**
37495      * @private The adapter to use to positon and resize elements
37496      */
37497     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37498     this.adapter.init(this);
37499     
37500     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37501         /** @private */
37502         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37503         this.el.addClass("roo-splitbar-h");
37504     }else{
37505         /** @private */
37506         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37507         this.el.addClass("roo-splitbar-v");
37508     }
37509     
37510     this.addEvents({
37511         /**
37512          * @event resize
37513          * Fires when the splitter is moved (alias for {@link #event-moved})
37514          * @param {Roo.bootstrap.SplitBar} this
37515          * @param {Number} newSize the new width or height
37516          */
37517         "resize" : true,
37518         /**
37519          * @event moved
37520          * Fires when the splitter is moved
37521          * @param {Roo.bootstrap.SplitBar} this
37522          * @param {Number} newSize the new width or height
37523          */
37524         "moved" : true,
37525         /**
37526          * @event beforeresize
37527          * Fires before the splitter is dragged
37528          * @param {Roo.bootstrap.SplitBar} this
37529          */
37530         "beforeresize" : true,
37531
37532         "beforeapply" : true
37533     });
37534
37535     Roo.util.Observable.call(this);
37536 };
37537
37538 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37539     onStartProxyDrag : function(x, y){
37540         this.fireEvent("beforeresize", this);
37541         if(!this.overlay){
37542             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37543             o.unselectable();
37544             o.enableDisplayMode("block");
37545             // all splitbars share the same overlay
37546             Roo.bootstrap.SplitBar.prototype.overlay = o;
37547         }
37548         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37549         this.overlay.show();
37550         Roo.get(this.proxy).setDisplayed("block");
37551         var size = this.adapter.getElementSize(this);
37552         this.activeMinSize = this.getMinimumSize();;
37553         this.activeMaxSize = this.getMaximumSize();;
37554         var c1 = size - this.activeMinSize;
37555         var c2 = Math.max(this.activeMaxSize - size, 0);
37556         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37557             this.dd.resetConstraints();
37558             this.dd.setXConstraint(
37559                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37560                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37561             );
37562             this.dd.setYConstraint(0, 0);
37563         }else{
37564             this.dd.resetConstraints();
37565             this.dd.setXConstraint(0, 0);
37566             this.dd.setYConstraint(
37567                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37568                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37569             );
37570          }
37571         this.dragSpecs.startSize = size;
37572         this.dragSpecs.startPoint = [x, y];
37573         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37574     },
37575     
37576     /** 
37577      * @private Called after the drag operation by the DDProxy
37578      */
37579     onEndProxyDrag : function(e){
37580         Roo.get(this.proxy).setDisplayed(false);
37581         var endPoint = Roo.lib.Event.getXY(e);
37582         if(this.overlay){
37583             this.overlay.hide();
37584         }
37585         var newSize;
37586         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37587             newSize = this.dragSpecs.startSize + 
37588                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37589                     endPoint[0] - this.dragSpecs.startPoint[0] :
37590                     this.dragSpecs.startPoint[0] - endPoint[0]
37591                 );
37592         }else{
37593             newSize = this.dragSpecs.startSize + 
37594                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37595                     endPoint[1] - this.dragSpecs.startPoint[1] :
37596                     this.dragSpecs.startPoint[1] - endPoint[1]
37597                 );
37598         }
37599         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37600         if(newSize != this.dragSpecs.startSize){
37601             if(this.fireEvent('beforeapply', this, newSize) !== false){
37602                 this.adapter.setElementSize(this, newSize);
37603                 this.fireEvent("moved", this, newSize);
37604                 this.fireEvent("resize", this, newSize);
37605             }
37606         }
37607     },
37608     
37609     /**
37610      * Get the adapter this SplitBar uses
37611      * @return The adapter object
37612      */
37613     getAdapter : function(){
37614         return this.adapter;
37615     },
37616     
37617     /**
37618      * Set the adapter this SplitBar uses
37619      * @param {Object} adapter A SplitBar adapter object
37620      */
37621     setAdapter : function(adapter){
37622         this.adapter = adapter;
37623         this.adapter.init(this);
37624     },
37625     
37626     /**
37627      * Gets the minimum size for the resizing element
37628      * @return {Number} The minimum size
37629      */
37630     getMinimumSize : function(){
37631         return this.minSize;
37632     },
37633     
37634     /**
37635      * Sets the minimum size for the resizing element
37636      * @param {Number} minSize The minimum size
37637      */
37638     setMinimumSize : function(minSize){
37639         this.minSize = minSize;
37640     },
37641     
37642     /**
37643      * Gets the maximum size for the resizing element
37644      * @return {Number} The maximum size
37645      */
37646     getMaximumSize : function(){
37647         return this.maxSize;
37648     },
37649     
37650     /**
37651      * Sets the maximum size for the resizing element
37652      * @param {Number} maxSize The maximum size
37653      */
37654     setMaximumSize : function(maxSize){
37655         this.maxSize = maxSize;
37656     },
37657     
37658     /**
37659      * Sets the initialize size for the resizing element
37660      * @param {Number} size The initial size
37661      */
37662     setCurrentSize : function(size){
37663         var oldAnimate = this.animate;
37664         this.animate = false;
37665         this.adapter.setElementSize(this, size);
37666         this.animate = oldAnimate;
37667     },
37668     
37669     /**
37670      * Destroy this splitbar. 
37671      * @param {Boolean} removeEl True to remove the element
37672      */
37673     destroy : function(removeEl){
37674         if(this.shim){
37675             this.shim.remove();
37676         }
37677         this.dd.unreg();
37678         this.proxy.parentNode.removeChild(this.proxy);
37679         if(removeEl){
37680             this.el.remove();
37681         }
37682     }
37683 });
37684
37685 /**
37686  * @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.
37687  */
37688 Roo.bootstrap.SplitBar.createProxy = function(dir){
37689     var proxy = new Roo.Element(document.createElement("div"));
37690     proxy.unselectable();
37691     var cls = 'roo-splitbar-proxy';
37692     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37693     document.body.appendChild(proxy.dom);
37694     return proxy.dom;
37695 };
37696
37697 /** 
37698  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37699  * Default Adapter. It assumes the splitter and resizing element are not positioned
37700  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37701  */
37702 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37703 };
37704
37705 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37706     // do nothing for now
37707     init : function(s){
37708     
37709     },
37710     /**
37711      * Called before drag operations to get the current size of the resizing element. 
37712      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37713      */
37714      getElementSize : function(s){
37715         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37716             return s.resizingEl.getWidth();
37717         }else{
37718             return s.resizingEl.getHeight();
37719         }
37720     },
37721     
37722     /**
37723      * Called after drag operations to set the size of the resizing element.
37724      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37725      * @param {Number} newSize The new size to set
37726      * @param {Function} onComplete A function to be invoked when resizing is complete
37727      */
37728     setElementSize : function(s, newSize, onComplete){
37729         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37730             if(!s.animate){
37731                 s.resizingEl.setWidth(newSize);
37732                 if(onComplete){
37733                     onComplete(s, newSize);
37734                 }
37735             }else{
37736                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37737             }
37738         }else{
37739             
37740             if(!s.animate){
37741                 s.resizingEl.setHeight(newSize);
37742                 if(onComplete){
37743                     onComplete(s, newSize);
37744                 }
37745             }else{
37746                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37747             }
37748         }
37749     }
37750 };
37751
37752 /** 
37753  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37754  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37755  * Adapter that  moves the splitter element to align with the resized sizing element. 
37756  * Used with an absolute positioned SplitBar.
37757  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37758  * document.body, make sure you assign an id to the body element.
37759  */
37760 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37761     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37762     this.container = Roo.get(container);
37763 };
37764
37765 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37766     init : function(s){
37767         this.basic.init(s);
37768     },
37769     
37770     getElementSize : function(s){
37771         return this.basic.getElementSize(s);
37772     },
37773     
37774     setElementSize : function(s, newSize, onComplete){
37775         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37776     },
37777     
37778     moveSplitter : function(s){
37779         var yes = Roo.bootstrap.SplitBar;
37780         switch(s.placement){
37781             case yes.LEFT:
37782                 s.el.setX(s.resizingEl.getRight());
37783                 break;
37784             case yes.RIGHT:
37785                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37786                 break;
37787             case yes.TOP:
37788                 s.el.setY(s.resizingEl.getBottom());
37789                 break;
37790             case yes.BOTTOM:
37791                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37792                 break;
37793         }
37794     }
37795 };
37796
37797 /**
37798  * Orientation constant - Create a vertical SplitBar
37799  * @static
37800  * @type Number
37801  */
37802 Roo.bootstrap.SplitBar.VERTICAL = 1;
37803
37804 /**
37805  * Orientation constant - Create a horizontal SplitBar
37806  * @static
37807  * @type Number
37808  */
37809 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37810
37811 /**
37812  * Placement constant - The resizing element is to the left of the splitter element
37813  * @static
37814  * @type Number
37815  */
37816 Roo.bootstrap.SplitBar.LEFT = 1;
37817
37818 /**
37819  * Placement constant - The resizing element is to the right of the splitter element
37820  * @static
37821  * @type Number
37822  */
37823 Roo.bootstrap.SplitBar.RIGHT = 2;
37824
37825 /**
37826  * Placement constant - The resizing element is positioned above the splitter element
37827  * @static
37828  * @type Number
37829  */
37830 Roo.bootstrap.SplitBar.TOP = 3;
37831
37832 /**
37833  * Placement constant - The resizing element is positioned under splitter element
37834  * @static
37835  * @type Number
37836  */
37837 Roo.bootstrap.SplitBar.BOTTOM = 4;
37838 /*
37839  * Based on:
37840  * Ext JS Library 1.1.1
37841  * Copyright(c) 2006-2007, Ext JS, LLC.
37842  *
37843  * Originally Released Under LGPL - original licence link has changed is not relivant.
37844  *
37845  * Fork - LGPL
37846  * <script type="text/javascript">
37847  */
37848
37849 /**
37850  * @class Roo.bootstrap.layout.Manager
37851  * @extends Roo.bootstrap.Component
37852  * @abstract
37853  * Base class for layout managers.
37854  */
37855 Roo.bootstrap.layout.Manager = function(config)
37856 {
37857     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37858
37859
37860
37861
37862
37863     /** false to disable window resize monitoring @type Boolean */
37864     this.monitorWindowResize = true;
37865     this.regions = {};
37866     this.addEvents({
37867         /**
37868          * @event layout
37869          * Fires when a layout is performed.
37870          * @param {Roo.LayoutManager} this
37871          */
37872         "layout" : true,
37873         /**
37874          * @event regionresized
37875          * Fires when the user resizes a region.
37876          * @param {Roo.LayoutRegion} region The resized region
37877          * @param {Number} newSize The new size (width for east/west, height for north/south)
37878          */
37879         "regionresized" : true,
37880         /**
37881          * @event regioncollapsed
37882          * Fires when a region is collapsed.
37883          * @param {Roo.LayoutRegion} region The collapsed region
37884          */
37885         "regioncollapsed" : true,
37886         /**
37887          * @event regionexpanded
37888          * Fires when a region is expanded.
37889          * @param {Roo.LayoutRegion} region The expanded region
37890          */
37891         "regionexpanded" : true
37892     });
37893     this.updating = false;
37894
37895     if (config.el) {
37896         this.el = Roo.get(config.el);
37897         this.initEvents();
37898     }
37899
37900 };
37901
37902 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37903
37904
37905     regions : null,
37906
37907     monitorWindowResize : true,
37908
37909
37910     updating : false,
37911
37912
37913     onRender : function(ct, position)
37914     {
37915         if(!this.el){
37916             this.el = Roo.get(ct);
37917             this.initEvents();
37918         }
37919         //this.fireEvent('render',this);
37920     },
37921
37922
37923     initEvents: function()
37924     {
37925
37926
37927         // ie scrollbar fix
37928         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37929             document.body.scroll = "no";
37930         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37931             this.el.position('relative');
37932         }
37933         this.id = this.el.id;
37934         this.el.addClass("roo-layout-container");
37935         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37936         if(this.el.dom != document.body ) {
37937             this.el.on('resize', this.layout,this);
37938             this.el.on('show', this.layout,this);
37939         }
37940
37941     },
37942
37943     /**
37944      * Returns true if this layout is currently being updated
37945      * @return {Boolean}
37946      */
37947     isUpdating : function(){
37948         return this.updating;
37949     },
37950
37951     /**
37952      * Suspend the LayoutManager from doing auto-layouts while
37953      * making multiple add or remove calls
37954      */
37955     beginUpdate : function(){
37956         this.updating = true;
37957     },
37958
37959     /**
37960      * Restore auto-layouts and optionally disable the manager from performing a layout
37961      * @param {Boolean} noLayout true to disable a layout update
37962      */
37963     endUpdate : function(noLayout){
37964         this.updating = false;
37965         if(!noLayout){
37966             this.layout();
37967         }
37968     },
37969
37970     layout: function(){
37971         // abstract...
37972     },
37973
37974     onRegionResized : function(region, newSize){
37975         this.fireEvent("regionresized", region, newSize);
37976         this.layout();
37977     },
37978
37979     onRegionCollapsed : function(region){
37980         this.fireEvent("regioncollapsed", region);
37981     },
37982
37983     onRegionExpanded : function(region){
37984         this.fireEvent("regionexpanded", region);
37985     },
37986
37987     /**
37988      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37989      * performs box-model adjustments.
37990      * @return {Object} The size as an object {width: (the width), height: (the height)}
37991      */
37992     getViewSize : function()
37993     {
37994         var size;
37995         if(this.el.dom != document.body){
37996             size = this.el.getSize();
37997         }else{
37998             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37999         }
38000         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38001         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38002         return size;
38003     },
38004
38005     /**
38006      * Returns the Element this layout is bound to.
38007      * @return {Roo.Element}
38008      */
38009     getEl : function(){
38010         return this.el;
38011     },
38012
38013     /**
38014      * Returns the specified region.
38015      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38016      * @return {Roo.LayoutRegion}
38017      */
38018     getRegion : function(target){
38019         return this.regions[target.toLowerCase()];
38020     },
38021
38022     onWindowResize : function(){
38023         if(this.monitorWindowResize){
38024             this.layout();
38025         }
38026     }
38027 });
38028 /*
38029  * Based on:
38030  * Ext JS Library 1.1.1
38031  * Copyright(c) 2006-2007, Ext JS, LLC.
38032  *
38033  * Originally Released Under LGPL - original licence link has changed is not relivant.
38034  *
38035  * Fork - LGPL
38036  * <script type="text/javascript">
38037  */
38038 /**
38039  * @class Roo.bootstrap.layout.Border
38040  * @extends Roo.bootstrap.layout.Manager
38041  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38042  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38043  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38044  * please see: examples/bootstrap/nested.html<br><br>
38045  
38046 <b>The container the layout is rendered into can be either the body element or any other element.
38047 If it is not the body element, the container needs to either be an absolute positioned element,
38048 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38049 the container size if it is not the body element.</b>
38050
38051 * @constructor
38052 * Create a new Border
38053 * @param {Object} config Configuration options
38054  */
38055 Roo.bootstrap.layout.Border = function(config){
38056     config = config || {};
38057     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38058     
38059     
38060     
38061     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38062         if(config[region]){
38063             config[region].region = region;
38064             this.addRegion(config[region]);
38065         }
38066     },this);
38067     
38068 };
38069
38070 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38071
38072 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38073     
38074         /**
38075          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38076          */
38077         /**
38078          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38079          */
38080         /**
38081          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38082          */
38083         /**
38084          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38085          */
38086         /**
38087          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38088          */
38089         
38090         
38091         
38092         
38093     parent : false, // this might point to a 'nest' or a ???
38094     
38095     /**
38096      * Creates and adds a new region if it doesn't already exist.
38097      * @param {String} target The target region key (north, south, east, west or center).
38098      * @param {Object} config The regions config object
38099      * @return {BorderLayoutRegion} The new region
38100      */
38101     addRegion : function(config)
38102     {
38103         if(!this.regions[config.region]){
38104             var r = this.factory(config);
38105             this.bindRegion(r);
38106         }
38107         return this.regions[config.region];
38108     },
38109
38110     // private (kinda)
38111     bindRegion : function(r){
38112         this.regions[r.config.region] = r;
38113         
38114         r.on("visibilitychange",    this.layout, this);
38115         r.on("paneladded",          this.layout, this);
38116         r.on("panelremoved",        this.layout, this);
38117         r.on("invalidated",         this.layout, this);
38118         r.on("resized",             this.onRegionResized, this);
38119         r.on("collapsed",           this.onRegionCollapsed, this);
38120         r.on("expanded",            this.onRegionExpanded, this);
38121     },
38122
38123     /**
38124      * Performs a layout update.
38125      */
38126     layout : function()
38127     {
38128         if(this.updating) {
38129             return;
38130         }
38131         
38132         // render all the rebions if they have not been done alreayd?
38133         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38134             if(this.regions[region] && !this.regions[region].bodyEl){
38135                 this.regions[region].onRender(this.el)
38136             }
38137         },this);
38138         
38139         var size = this.getViewSize();
38140         var w = size.width;
38141         var h = size.height;
38142         var centerW = w;
38143         var centerH = h;
38144         var centerY = 0;
38145         var centerX = 0;
38146         //var x = 0, y = 0;
38147
38148         var rs = this.regions;
38149         var north = rs["north"];
38150         var south = rs["south"]; 
38151         var west = rs["west"];
38152         var east = rs["east"];
38153         var center = rs["center"];
38154         //if(this.hideOnLayout){ // not supported anymore
38155             //c.el.setStyle("display", "none");
38156         //}
38157         if(north && north.isVisible()){
38158             var b = north.getBox();
38159             var m = north.getMargins();
38160             b.width = w - (m.left+m.right);
38161             b.x = m.left;
38162             b.y = m.top;
38163             centerY = b.height + b.y + m.bottom;
38164             centerH -= centerY;
38165             north.updateBox(this.safeBox(b));
38166         }
38167         if(south && south.isVisible()){
38168             var b = south.getBox();
38169             var m = south.getMargins();
38170             b.width = w - (m.left+m.right);
38171             b.x = m.left;
38172             var totalHeight = (b.height + m.top + m.bottom);
38173             b.y = h - totalHeight + m.top;
38174             centerH -= totalHeight;
38175             south.updateBox(this.safeBox(b));
38176         }
38177         if(west && west.isVisible()){
38178             var b = west.getBox();
38179             var m = west.getMargins();
38180             b.height = centerH - (m.top+m.bottom);
38181             b.x = m.left;
38182             b.y = centerY + m.top;
38183             var totalWidth = (b.width + m.left + m.right);
38184             centerX += totalWidth;
38185             centerW -= totalWidth;
38186             west.updateBox(this.safeBox(b));
38187         }
38188         if(east && east.isVisible()){
38189             var b = east.getBox();
38190             var m = east.getMargins();
38191             b.height = centerH - (m.top+m.bottom);
38192             var totalWidth = (b.width + m.left + m.right);
38193             b.x = w - totalWidth + m.left;
38194             b.y = centerY + m.top;
38195             centerW -= totalWidth;
38196             east.updateBox(this.safeBox(b));
38197         }
38198         if(center){
38199             var m = center.getMargins();
38200             var centerBox = {
38201                 x: centerX + m.left,
38202                 y: centerY + m.top,
38203                 width: centerW - (m.left+m.right),
38204                 height: centerH - (m.top+m.bottom)
38205             };
38206             //if(this.hideOnLayout){
38207                 //center.el.setStyle("display", "block");
38208             //}
38209             center.updateBox(this.safeBox(centerBox));
38210         }
38211         this.el.repaint();
38212         this.fireEvent("layout", this);
38213     },
38214
38215     // private
38216     safeBox : function(box){
38217         box.width = Math.max(0, box.width);
38218         box.height = Math.max(0, box.height);
38219         return box;
38220     },
38221
38222     /**
38223      * Adds a ContentPanel (or subclass) to this layout.
38224      * @param {String} target The target region key (north, south, east, west or center).
38225      * @param {Roo.ContentPanel} panel The panel to add
38226      * @return {Roo.ContentPanel} The added panel
38227      */
38228     add : function(target, panel){
38229          
38230         target = target.toLowerCase();
38231         return this.regions[target].add(panel);
38232     },
38233
38234     /**
38235      * Remove a ContentPanel (or subclass) to this layout.
38236      * @param {String} target The target region key (north, south, east, west or center).
38237      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38238      * @return {Roo.ContentPanel} The removed panel
38239      */
38240     remove : function(target, panel){
38241         target = target.toLowerCase();
38242         return this.regions[target].remove(panel);
38243     },
38244
38245     /**
38246      * Searches all regions for a panel with the specified id
38247      * @param {String} panelId
38248      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38249      */
38250     findPanel : function(panelId){
38251         var rs = this.regions;
38252         for(var target in rs){
38253             if(typeof rs[target] != "function"){
38254                 var p = rs[target].getPanel(panelId);
38255                 if(p){
38256                     return p;
38257                 }
38258             }
38259         }
38260         return null;
38261     },
38262
38263     /**
38264      * Searches all regions for a panel with the specified id and activates (shows) it.
38265      * @param {String/ContentPanel} panelId The panels id or the panel itself
38266      * @return {Roo.ContentPanel} The shown panel or null
38267      */
38268     showPanel : function(panelId) {
38269       var rs = this.regions;
38270       for(var target in rs){
38271          var r = rs[target];
38272          if(typeof r != "function"){
38273             if(r.hasPanel(panelId)){
38274                return r.showPanel(panelId);
38275             }
38276          }
38277       }
38278       return null;
38279    },
38280
38281    /**
38282      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38283      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38284      */
38285    /*
38286     restoreState : function(provider){
38287         if(!provider){
38288             provider = Roo.state.Manager;
38289         }
38290         var sm = new Roo.LayoutStateManager();
38291         sm.init(this, provider);
38292     },
38293 */
38294  
38295  
38296     /**
38297      * Adds a xtype elements to the layout.
38298      * <pre><code>
38299
38300 layout.addxtype({
38301        xtype : 'ContentPanel',
38302        region: 'west',
38303        items: [ .... ]
38304    }
38305 );
38306
38307 layout.addxtype({
38308         xtype : 'NestedLayoutPanel',
38309         region: 'west',
38310         layout: {
38311            center: { },
38312            west: { }   
38313         },
38314         items : [ ... list of content panels or nested layout panels.. ]
38315    }
38316 );
38317 </code></pre>
38318      * @param {Object} cfg Xtype definition of item to add.
38319      */
38320     addxtype : function(cfg)
38321     {
38322         // basically accepts a pannel...
38323         // can accept a layout region..!?!?
38324         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38325         
38326         
38327         // theory?  children can only be panels??
38328         
38329         //if (!cfg.xtype.match(/Panel$/)) {
38330         //    return false;
38331         //}
38332         var ret = false;
38333         
38334         if (typeof(cfg.region) == 'undefined') {
38335             Roo.log("Failed to add Panel, region was not set");
38336             Roo.log(cfg);
38337             return false;
38338         }
38339         var region = cfg.region;
38340         delete cfg.region;
38341         
38342           
38343         var xitems = [];
38344         if (cfg.items) {
38345             xitems = cfg.items;
38346             delete cfg.items;
38347         }
38348         var nb = false;
38349         
38350         if ( region == 'center') {
38351             Roo.log("Center: " + cfg.title);
38352         }
38353         
38354         
38355         switch(cfg.xtype) 
38356         {
38357             case 'Content':  // ContentPanel (el, cfg)
38358             case 'Scroll':  // ContentPanel (el, cfg)
38359             case 'View': 
38360                 cfg.autoCreate = cfg.autoCreate || true;
38361                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38362                 //} else {
38363                 //    var el = this.el.createChild();
38364                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38365                 //}
38366                 
38367                 this.add(region, ret);
38368                 break;
38369             
38370             /*
38371             case 'TreePanel': // our new panel!
38372                 cfg.el = this.el.createChild();
38373                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38374                 this.add(region, ret);
38375                 break;
38376             */
38377             
38378             case 'Nest': 
38379                 // create a new Layout (which is  a Border Layout...
38380                 
38381                 var clayout = cfg.layout;
38382                 clayout.el  = this.el.createChild();
38383                 clayout.items   = clayout.items  || [];
38384                 
38385                 delete cfg.layout;
38386                 
38387                 // replace this exitems with the clayout ones..
38388                 xitems = clayout.items;
38389                  
38390                 // force background off if it's in center...
38391                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38392                     cfg.background = false;
38393                 }
38394                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38395                 
38396                 
38397                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38398                 //console.log('adding nested layout panel '  + cfg.toSource());
38399                 this.add(region, ret);
38400                 nb = {}; /// find first...
38401                 break;
38402             
38403             case 'Grid':
38404                 
38405                 // needs grid and region
38406                 
38407                 //var el = this.getRegion(region).el.createChild();
38408                 /*
38409                  *var el = this.el.createChild();
38410                 // create the grid first...
38411                 cfg.grid.container = el;
38412                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38413                 */
38414                 
38415                 if (region == 'center' && this.active ) {
38416                     cfg.background = false;
38417                 }
38418                 
38419                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38420                 
38421                 this.add(region, ret);
38422                 /*
38423                 if (cfg.background) {
38424                     // render grid on panel activation (if panel background)
38425                     ret.on('activate', function(gp) {
38426                         if (!gp.grid.rendered) {
38427                     //        gp.grid.render(el);
38428                         }
38429                     });
38430                 } else {
38431                   //  cfg.grid.render(el);
38432                 }
38433                 */
38434                 break;
38435            
38436            
38437             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38438                 // it was the old xcomponent building that caused this before.
38439                 // espeically if border is the top element in the tree.
38440                 ret = this;
38441                 break; 
38442                 
38443                     
38444                 
38445                 
38446                 
38447             default:
38448                 /*
38449                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38450                     
38451                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38452                     this.add(region, ret);
38453                 } else {
38454                 */
38455                     Roo.log(cfg);
38456                     throw "Can not add '" + cfg.xtype + "' to Border";
38457                     return null;
38458              
38459                                 
38460              
38461         }
38462         this.beginUpdate();
38463         // add children..
38464         var region = '';
38465         var abn = {};
38466         Roo.each(xitems, function(i)  {
38467             region = nb && i.region ? i.region : false;
38468             
38469             var add = ret.addxtype(i);
38470            
38471             if (region) {
38472                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38473                 if (!i.background) {
38474                     abn[region] = nb[region] ;
38475                 }
38476             }
38477             
38478         });
38479         this.endUpdate();
38480
38481         // make the last non-background panel active..
38482         //if (nb) { Roo.log(abn); }
38483         if (nb) {
38484             
38485             for(var r in abn) {
38486                 region = this.getRegion(r);
38487                 if (region) {
38488                     // tried using nb[r], but it does not work..
38489                      
38490                     region.showPanel(abn[r]);
38491                    
38492                 }
38493             }
38494         }
38495         return ret;
38496         
38497     },
38498     
38499     
38500 // private
38501     factory : function(cfg)
38502     {
38503         
38504         var validRegions = Roo.bootstrap.layout.Border.regions;
38505
38506         var target = cfg.region;
38507         cfg.mgr = this;
38508         
38509         var r = Roo.bootstrap.layout;
38510         Roo.log(target);
38511         switch(target){
38512             case "north":
38513                 return new r.North(cfg);
38514             case "south":
38515                 return new r.South(cfg);
38516             case "east":
38517                 return new r.East(cfg);
38518             case "west":
38519                 return new r.West(cfg);
38520             case "center":
38521                 return new r.Center(cfg);
38522         }
38523         throw 'Layout region "'+target+'" not supported.';
38524     }
38525     
38526     
38527 });
38528  /*
38529  * Based on:
38530  * Ext JS Library 1.1.1
38531  * Copyright(c) 2006-2007, Ext JS, LLC.
38532  *
38533  * Originally Released Under LGPL - original licence link has changed is not relivant.
38534  *
38535  * Fork - LGPL
38536  * <script type="text/javascript">
38537  */
38538  
38539 /**
38540  * @class Roo.bootstrap.layout.Basic
38541  * @extends Roo.util.Observable
38542  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38543  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38544  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38545  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38546  * @cfg {string}   region  the region that it inhabits..
38547  * @cfg {bool}   skipConfig skip config?
38548  * 
38549
38550  */
38551 Roo.bootstrap.layout.Basic = function(config){
38552     
38553     this.mgr = config.mgr;
38554     
38555     this.position = config.region;
38556     
38557     var skipConfig = config.skipConfig;
38558     
38559     this.events = {
38560         /**
38561          * @scope Roo.BasicLayoutRegion
38562          */
38563         
38564         /**
38565          * @event beforeremove
38566          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38567          * @param {Roo.LayoutRegion} this
38568          * @param {Roo.ContentPanel} panel The panel
38569          * @param {Object} e The cancel event object
38570          */
38571         "beforeremove" : true,
38572         /**
38573          * @event invalidated
38574          * Fires when the layout for this region is changed.
38575          * @param {Roo.LayoutRegion} this
38576          */
38577         "invalidated" : true,
38578         /**
38579          * @event visibilitychange
38580          * Fires when this region is shown or hidden 
38581          * @param {Roo.LayoutRegion} this
38582          * @param {Boolean} visibility true or false
38583          */
38584         "visibilitychange" : true,
38585         /**
38586          * @event paneladded
38587          * Fires when a panel is added. 
38588          * @param {Roo.LayoutRegion} this
38589          * @param {Roo.ContentPanel} panel The panel
38590          */
38591         "paneladded" : true,
38592         /**
38593          * @event panelremoved
38594          * Fires when a panel is removed. 
38595          * @param {Roo.LayoutRegion} this
38596          * @param {Roo.ContentPanel} panel The panel
38597          */
38598         "panelremoved" : true,
38599         /**
38600          * @event beforecollapse
38601          * Fires when this region before collapse.
38602          * @param {Roo.LayoutRegion} this
38603          */
38604         "beforecollapse" : true,
38605         /**
38606          * @event collapsed
38607          * Fires when this region is collapsed.
38608          * @param {Roo.LayoutRegion} this
38609          */
38610         "collapsed" : true,
38611         /**
38612          * @event expanded
38613          * Fires when this region is expanded.
38614          * @param {Roo.LayoutRegion} this
38615          */
38616         "expanded" : true,
38617         /**
38618          * @event slideshow
38619          * Fires when this region is slid into view.
38620          * @param {Roo.LayoutRegion} this
38621          */
38622         "slideshow" : true,
38623         /**
38624          * @event slidehide
38625          * Fires when this region slides out of view. 
38626          * @param {Roo.LayoutRegion} this
38627          */
38628         "slidehide" : true,
38629         /**
38630          * @event panelactivated
38631          * Fires when a panel is activated. 
38632          * @param {Roo.LayoutRegion} this
38633          * @param {Roo.ContentPanel} panel The activated panel
38634          */
38635         "panelactivated" : true,
38636         /**
38637          * @event resized
38638          * Fires when the user resizes this region. 
38639          * @param {Roo.LayoutRegion} this
38640          * @param {Number} newSize The new size (width for east/west, height for north/south)
38641          */
38642         "resized" : true
38643     };
38644     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38645     this.panels = new Roo.util.MixedCollection();
38646     this.panels.getKey = this.getPanelId.createDelegate(this);
38647     this.box = null;
38648     this.activePanel = null;
38649     // ensure listeners are added...
38650     
38651     if (config.listeners || config.events) {
38652         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38653             listeners : config.listeners || {},
38654             events : config.events || {}
38655         });
38656     }
38657     
38658     if(skipConfig !== true){
38659         this.applyConfig(config);
38660     }
38661 };
38662
38663 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38664 {
38665     getPanelId : function(p){
38666         return p.getId();
38667     },
38668     
38669     applyConfig : function(config){
38670         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38671         this.config = config;
38672         
38673     },
38674     
38675     /**
38676      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38677      * the width, for horizontal (north, south) the height.
38678      * @param {Number} newSize The new width or height
38679      */
38680     resizeTo : function(newSize){
38681         var el = this.el ? this.el :
38682                  (this.activePanel ? this.activePanel.getEl() : null);
38683         if(el){
38684             switch(this.position){
38685                 case "east":
38686                 case "west":
38687                     el.setWidth(newSize);
38688                     this.fireEvent("resized", this, newSize);
38689                 break;
38690                 case "north":
38691                 case "south":
38692                     el.setHeight(newSize);
38693                     this.fireEvent("resized", this, newSize);
38694                 break;                
38695             }
38696         }
38697     },
38698     
38699     getBox : function(){
38700         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38701     },
38702     
38703     getMargins : function(){
38704         return this.margins;
38705     },
38706     
38707     updateBox : function(box){
38708         this.box = box;
38709         var el = this.activePanel.getEl();
38710         el.dom.style.left = box.x + "px";
38711         el.dom.style.top = box.y + "px";
38712         this.activePanel.setSize(box.width, box.height);
38713     },
38714     
38715     /**
38716      * Returns the container element for this region.
38717      * @return {Roo.Element}
38718      */
38719     getEl : function(){
38720         return this.activePanel;
38721     },
38722     
38723     /**
38724      * Returns true if this region is currently visible.
38725      * @return {Boolean}
38726      */
38727     isVisible : function(){
38728         return this.activePanel ? true : false;
38729     },
38730     
38731     setActivePanel : function(panel){
38732         panel = this.getPanel(panel);
38733         if(this.activePanel && this.activePanel != panel){
38734             this.activePanel.setActiveState(false);
38735             this.activePanel.getEl().setLeftTop(-10000,-10000);
38736         }
38737         this.activePanel = panel;
38738         panel.setActiveState(true);
38739         if(this.box){
38740             panel.setSize(this.box.width, this.box.height);
38741         }
38742         this.fireEvent("panelactivated", this, panel);
38743         this.fireEvent("invalidated");
38744     },
38745     
38746     /**
38747      * Show the specified panel.
38748      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38749      * @return {Roo.ContentPanel} The shown panel or null
38750      */
38751     showPanel : function(panel){
38752         panel = this.getPanel(panel);
38753         if(panel){
38754             this.setActivePanel(panel);
38755         }
38756         return panel;
38757     },
38758     
38759     /**
38760      * Get the active panel for this region.
38761      * @return {Roo.ContentPanel} The active panel or null
38762      */
38763     getActivePanel : function(){
38764         return this.activePanel;
38765     },
38766     
38767     /**
38768      * Add the passed ContentPanel(s)
38769      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38770      * @return {Roo.ContentPanel} The panel added (if only one was added)
38771      */
38772     add : function(panel){
38773         if(arguments.length > 1){
38774             for(var i = 0, len = arguments.length; i < len; i++) {
38775                 this.add(arguments[i]);
38776             }
38777             return null;
38778         }
38779         if(this.hasPanel(panel)){
38780             this.showPanel(panel);
38781             return panel;
38782         }
38783         var el = panel.getEl();
38784         if(el.dom.parentNode != this.mgr.el.dom){
38785             this.mgr.el.dom.appendChild(el.dom);
38786         }
38787         if(panel.setRegion){
38788             panel.setRegion(this);
38789         }
38790         this.panels.add(panel);
38791         el.setStyle("position", "absolute");
38792         if(!panel.background){
38793             this.setActivePanel(panel);
38794             if(this.config.initialSize && this.panels.getCount()==1){
38795                 this.resizeTo(this.config.initialSize);
38796             }
38797         }
38798         this.fireEvent("paneladded", this, panel);
38799         return panel;
38800     },
38801     
38802     /**
38803      * Returns true if the panel is in this region.
38804      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38805      * @return {Boolean}
38806      */
38807     hasPanel : function(panel){
38808         if(typeof panel == "object"){ // must be panel obj
38809             panel = panel.getId();
38810         }
38811         return this.getPanel(panel) ? true : false;
38812     },
38813     
38814     /**
38815      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38816      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38817      * @param {Boolean} preservePanel Overrides the config preservePanel option
38818      * @return {Roo.ContentPanel} The panel that was removed
38819      */
38820     remove : function(panel, preservePanel){
38821         panel = this.getPanel(panel);
38822         if(!panel){
38823             return null;
38824         }
38825         var e = {};
38826         this.fireEvent("beforeremove", this, panel, e);
38827         if(e.cancel === true){
38828             return null;
38829         }
38830         var panelId = panel.getId();
38831         this.panels.removeKey(panelId);
38832         return panel;
38833     },
38834     
38835     /**
38836      * Returns the panel specified or null if it's not in this region.
38837      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38838      * @return {Roo.ContentPanel}
38839      */
38840     getPanel : function(id){
38841         if(typeof id == "object"){ // must be panel obj
38842             return id;
38843         }
38844         return this.panels.get(id);
38845     },
38846     
38847     /**
38848      * Returns this regions position (north/south/east/west/center).
38849      * @return {String} 
38850      */
38851     getPosition: function(){
38852         return this.position;    
38853     }
38854 });/*
38855  * Based on:
38856  * Ext JS Library 1.1.1
38857  * Copyright(c) 2006-2007, Ext JS, LLC.
38858  *
38859  * Originally Released Under LGPL - original licence link has changed is not relivant.
38860  *
38861  * Fork - LGPL
38862  * <script type="text/javascript">
38863  */
38864  
38865 /**
38866  * @class Roo.bootstrap.layout.Region
38867  * @extends Roo.bootstrap.layout.Basic
38868  * This class represents a region in a layout manager.
38869  
38870  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38871  * @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})
38872  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38873  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38874  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38875  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38876  * @cfg {String}    title           The title for the region (overrides panel titles)
38877  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38878  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38879  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38880  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38881  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38882  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38883  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38884  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38885  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38886  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38887
38888  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38889  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38890  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38891  * @cfg {Number}    width           For East/West panels
38892  * @cfg {Number}    height          For North/South panels
38893  * @cfg {Boolean}   split           To show the splitter
38894  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38895  * 
38896  * @cfg {string}   cls             Extra CSS classes to add to region
38897  * 
38898  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38899  * @cfg {string}   region  the region that it inhabits..
38900  *
38901
38902  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38903  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38904
38905  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38906  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38907  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38908  */
38909 Roo.bootstrap.layout.Region = function(config)
38910 {
38911     this.applyConfig(config);
38912
38913     var mgr = config.mgr;
38914     var pos = config.region;
38915     config.skipConfig = true;
38916     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38917     
38918     if (mgr.el) {
38919         this.onRender(mgr.el);   
38920     }
38921      
38922     this.visible = true;
38923     this.collapsed = false;
38924     this.unrendered_panels = [];
38925 };
38926
38927 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38928
38929     position: '', // set by wrapper (eg. north/south etc..)
38930     unrendered_panels : null,  // unrendered panels.
38931     
38932     tabPosition : false,
38933     
38934     mgr: false, // points to 'Border'
38935     
38936     
38937     createBody : function(){
38938         /** This region's body element 
38939         * @type Roo.Element */
38940         this.bodyEl = this.el.createChild({
38941                 tag: "div",
38942                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38943         });
38944     },
38945
38946     onRender: function(ctr, pos)
38947     {
38948         var dh = Roo.DomHelper;
38949         /** This region's container element 
38950         * @type Roo.Element */
38951         this.el = dh.append(ctr.dom, {
38952                 tag: "div",
38953                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38954             }, true);
38955         /** This region's title element 
38956         * @type Roo.Element */
38957     
38958         this.titleEl = dh.append(this.el.dom,  {
38959                 tag: "div",
38960                 unselectable: "on",
38961                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38962                 children:[
38963                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38964                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38965                 ]
38966             }, true);
38967         
38968         this.titleEl.enableDisplayMode();
38969         /** This region's title text element 
38970         * @type HTMLElement */
38971         this.titleTextEl = this.titleEl.dom.firstChild;
38972         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38973         /*
38974         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38975         this.closeBtn.enableDisplayMode();
38976         this.closeBtn.on("click", this.closeClicked, this);
38977         this.closeBtn.hide();
38978     */
38979         this.createBody(this.config);
38980         if(this.config.hideWhenEmpty){
38981             this.hide();
38982             this.on("paneladded", this.validateVisibility, this);
38983             this.on("panelremoved", this.validateVisibility, this);
38984         }
38985         if(this.autoScroll){
38986             this.bodyEl.setStyle("overflow", "auto");
38987         }else{
38988             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38989         }
38990         //if(c.titlebar !== false){
38991             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38992                 this.titleEl.hide();
38993             }else{
38994                 this.titleEl.show();
38995                 if(this.config.title){
38996                     this.titleTextEl.innerHTML = this.config.title;
38997                 }
38998             }
38999         //}
39000         if(this.config.collapsed){
39001             this.collapse(true);
39002         }
39003         if(this.config.hidden){
39004             this.hide();
39005         }
39006         
39007         if (this.unrendered_panels && this.unrendered_panels.length) {
39008             for (var i =0;i< this.unrendered_panels.length; i++) {
39009                 this.add(this.unrendered_panels[i]);
39010             }
39011             this.unrendered_panels = null;
39012             
39013         }
39014         
39015     },
39016     
39017     applyConfig : function(c)
39018     {
39019         /*
39020          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39021             var dh = Roo.DomHelper;
39022             if(c.titlebar !== false){
39023                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39024                 this.collapseBtn.on("click", this.collapse, this);
39025                 this.collapseBtn.enableDisplayMode();
39026                 /*
39027                 if(c.showPin === true || this.showPin){
39028                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39029                     this.stickBtn.enableDisplayMode();
39030                     this.stickBtn.on("click", this.expand, this);
39031                     this.stickBtn.hide();
39032                 }
39033                 
39034             }
39035             */
39036             /** This region's collapsed element
39037             * @type Roo.Element */
39038             /*
39039              *
39040             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39041                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39042             ]}, true);
39043             
39044             if(c.floatable !== false){
39045                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39046                this.collapsedEl.on("click", this.collapseClick, this);
39047             }
39048
39049             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39050                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39051                    id: "message", unselectable: "on", style:{"float":"left"}});
39052                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39053              }
39054             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39055             this.expandBtn.on("click", this.expand, this);
39056             
39057         }
39058         
39059         if(this.collapseBtn){
39060             this.collapseBtn.setVisible(c.collapsible == true);
39061         }
39062         
39063         this.cmargins = c.cmargins || this.cmargins ||
39064                          (this.position == "west" || this.position == "east" ?
39065                              {top: 0, left: 2, right:2, bottom: 0} :
39066                              {top: 2, left: 0, right:0, bottom: 2});
39067         */
39068         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39069         
39070         
39071         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39072         
39073         this.autoScroll = c.autoScroll || false;
39074         
39075         
39076        
39077         
39078         this.duration = c.duration || .30;
39079         this.slideDuration = c.slideDuration || .45;
39080         this.config = c;
39081        
39082     },
39083     /**
39084      * Returns true if this region is currently visible.
39085      * @return {Boolean}
39086      */
39087     isVisible : function(){
39088         return this.visible;
39089     },
39090
39091     /**
39092      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39093      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39094      */
39095     //setCollapsedTitle : function(title){
39096     //    title = title || "&#160;";
39097      //   if(this.collapsedTitleTextEl){
39098       //      this.collapsedTitleTextEl.innerHTML = title;
39099        // }
39100     //},
39101
39102     getBox : function(){
39103         var b;
39104       //  if(!this.collapsed){
39105             b = this.el.getBox(false, true);
39106        // }else{
39107           //  b = this.collapsedEl.getBox(false, true);
39108         //}
39109         return b;
39110     },
39111
39112     getMargins : function(){
39113         return this.margins;
39114         //return this.collapsed ? this.cmargins : this.margins;
39115     },
39116 /*
39117     highlight : function(){
39118         this.el.addClass("x-layout-panel-dragover");
39119     },
39120
39121     unhighlight : function(){
39122         this.el.removeClass("x-layout-panel-dragover");
39123     },
39124 */
39125     updateBox : function(box)
39126     {
39127         if (!this.bodyEl) {
39128             return; // not rendered yet..
39129         }
39130         
39131         this.box = box;
39132         if(!this.collapsed){
39133             this.el.dom.style.left = box.x + "px";
39134             this.el.dom.style.top = box.y + "px";
39135             this.updateBody(box.width, box.height);
39136         }else{
39137             this.collapsedEl.dom.style.left = box.x + "px";
39138             this.collapsedEl.dom.style.top = box.y + "px";
39139             this.collapsedEl.setSize(box.width, box.height);
39140         }
39141         if(this.tabs){
39142             this.tabs.autoSizeTabs();
39143         }
39144     },
39145
39146     updateBody : function(w, h)
39147     {
39148         if(w !== null){
39149             this.el.setWidth(w);
39150             w -= this.el.getBorderWidth("rl");
39151             if(this.config.adjustments){
39152                 w += this.config.adjustments[0];
39153             }
39154         }
39155         if(h !== null && h > 0){
39156             this.el.setHeight(h);
39157             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39158             h -= this.el.getBorderWidth("tb");
39159             if(this.config.adjustments){
39160                 h += this.config.adjustments[1];
39161             }
39162             this.bodyEl.setHeight(h);
39163             if(this.tabs){
39164                 h = this.tabs.syncHeight(h);
39165             }
39166         }
39167         if(this.panelSize){
39168             w = w !== null ? w : this.panelSize.width;
39169             h = h !== null ? h : this.panelSize.height;
39170         }
39171         if(this.activePanel){
39172             var el = this.activePanel.getEl();
39173             w = w !== null ? w : el.getWidth();
39174             h = h !== null ? h : el.getHeight();
39175             this.panelSize = {width: w, height: h};
39176             this.activePanel.setSize(w, h);
39177         }
39178         if(Roo.isIE && this.tabs){
39179             this.tabs.el.repaint();
39180         }
39181     },
39182
39183     /**
39184      * Returns the container element for this region.
39185      * @return {Roo.Element}
39186      */
39187     getEl : function(){
39188         return this.el;
39189     },
39190
39191     /**
39192      * Hides this region.
39193      */
39194     hide : function(){
39195         //if(!this.collapsed){
39196             this.el.dom.style.left = "-2000px";
39197             this.el.hide();
39198         //}else{
39199          //   this.collapsedEl.dom.style.left = "-2000px";
39200          //   this.collapsedEl.hide();
39201        // }
39202         this.visible = false;
39203         this.fireEvent("visibilitychange", this, false);
39204     },
39205
39206     /**
39207      * Shows this region if it was previously hidden.
39208      */
39209     show : function(){
39210         //if(!this.collapsed){
39211             this.el.show();
39212         //}else{
39213         //    this.collapsedEl.show();
39214        // }
39215         this.visible = true;
39216         this.fireEvent("visibilitychange", this, true);
39217     },
39218 /*
39219     closeClicked : function(){
39220         if(this.activePanel){
39221             this.remove(this.activePanel);
39222         }
39223     },
39224
39225     collapseClick : function(e){
39226         if(this.isSlid){
39227            e.stopPropagation();
39228            this.slideIn();
39229         }else{
39230            e.stopPropagation();
39231            this.slideOut();
39232         }
39233     },
39234 */
39235     /**
39236      * Collapses this region.
39237      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39238      */
39239     /*
39240     collapse : function(skipAnim, skipCheck = false){
39241         if(this.collapsed) {
39242             return;
39243         }
39244         
39245         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39246             
39247             this.collapsed = true;
39248             if(this.split){
39249                 this.split.el.hide();
39250             }
39251             if(this.config.animate && skipAnim !== true){
39252                 this.fireEvent("invalidated", this);
39253                 this.animateCollapse();
39254             }else{
39255                 this.el.setLocation(-20000,-20000);
39256                 this.el.hide();
39257                 this.collapsedEl.show();
39258                 this.fireEvent("collapsed", this);
39259                 this.fireEvent("invalidated", this);
39260             }
39261         }
39262         
39263     },
39264 */
39265     animateCollapse : function(){
39266         // overridden
39267     },
39268
39269     /**
39270      * Expands this region if it was previously collapsed.
39271      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39272      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39273      */
39274     /*
39275     expand : function(e, skipAnim){
39276         if(e) {
39277             e.stopPropagation();
39278         }
39279         if(!this.collapsed || this.el.hasActiveFx()) {
39280             return;
39281         }
39282         if(this.isSlid){
39283             this.afterSlideIn();
39284             skipAnim = true;
39285         }
39286         this.collapsed = false;
39287         if(this.config.animate && skipAnim !== true){
39288             this.animateExpand();
39289         }else{
39290             this.el.show();
39291             if(this.split){
39292                 this.split.el.show();
39293             }
39294             this.collapsedEl.setLocation(-2000,-2000);
39295             this.collapsedEl.hide();
39296             this.fireEvent("invalidated", this);
39297             this.fireEvent("expanded", this);
39298         }
39299     },
39300 */
39301     animateExpand : function(){
39302         // overridden
39303     },
39304
39305     initTabs : function()
39306     {
39307         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39308         
39309         var ts = new Roo.bootstrap.panel.Tabs({
39310             el: this.bodyEl.dom,
39311             region : this,
39312             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39313             disableTooltips: this.config.disableTabTips,
39314             toolbar : this.config.toolbar
39315         });
39316         
39317         if(this.config.hideTabs){
39318             ts.stripWrap.setDisplayed(false);
39319         }
39320         this.tabs = ts;
39321         ts.resizeTabs = this.config.resizeTabs === true;
39322         ts.minTabWidth = this.config.minTabWidth || 40;
39323         ts.maxTabWidth = this.config.maxTabWidth || 250;
39324         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39325         ts.monitorResize = false;
39326         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39327         ts.bodyEl.addClass('roo-layout-tabs-body');
39328         this.panels.each(this.initPanelAsTab, this);
39329     },
39330
39331     initPanelAsTab : function(panel){
39332         var ti = this.tabs.addTab(
39333             panel.getEl().id,
39334             panel.getTitle(),
39335             null,
39336             this.config.closeOnTab && panel.isClosable(),
39337             panel.tpl
39338         );
39339         if(panel.tabTip !== undefined){
39340             ti.setTooltip(panel.tabTip);
39341         }
39342         ti.on("activate", function(){
39343               this.setActivePanel(panel);
39344         }, this);
39345         
39346         if(this.config.closeOnTab){
39347             ti.on("beforeclose", function(t, e){
39348                 e.cancel = true;
39349                 this.remove(panel);
39350             }, this);
39351         }
39352         
39353         panel.tabItem = ti;
39354         
39355         return ti;
39356     },
39357
39358     updatePanelTitle : function(panel, title)
39359     {
39360         if(this.activePanel == panel){
39361             this.updateTitle(title);
39362         }
39363         if(this.tabs){
39364             var ti = this.tabs.getTab(panel.getEl().id);
39365             ti.setText(title);
39366             if(panel.tabTip !== undefined){
39367                 ti.setTooltip(panel.tabTip);
39368             }
39369         }
39370     },
39371
39372     updateTitle : function(title){
39373         if(this.titleTextEl && !this.config.title){
39374             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39375         }
39376     },
39377
39378     setActivePanel : function(panel)
39379     {
39380         panel = this.getPanel(panel);
39381         if(this.activePanel && this.activePanel != panel){
39382             if(this.activePanel.setActiveState(false) === false){
39383                 return;
39384             }
39385         }
39386         this.activePanel = panel;
39387         panel.setActiveState(true);
39388         if(this.panelSize){
39389             panel.setSize(this.panelSize.width, this.panelSize.height);
39390         }
39391         if(this.closeBtn){
39392             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39393         }
39394         this.updateTitle(panel.getTitle());
39395         if(this.tabs){
39396             this.fireEvent("invalidated", this);
39397         }
39398         this.fireEvent("panelactivated", this, panel);
39399     },
39400
39401     /**
39402      * Shows the specified panel.
39403      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39404      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39405      */
39406     showPanel : function(panel)
39407     {
39408         panel = this.getPanel(panel);
39409         if(panel){
39410             if(this.tabs){
39411                 var tab = this.tabs.getTab(panel.getEl().id);
39412                 if(tab.isHidden()){
39413                     this.tabs.unhideTab(tab.id);
39414                 }
39415                 tab.activate();
39416             }else{
39417                 this.setActivePanel(panel);
39418             }
39419         }
39420         return panel;
39421     },
39422
39423     /**
39424      * Get the active panel for this region.
39425      * @return {Roo.ContentPanel} The active panel or null
39426      */
39427     getActivePanel : function(){
39428         return this.activePanel;
39429     },
39430
39431     validateVisibility : function(){
39432         if(this.panels.getCount() < 1){
39433             this.updateTitle("&#160;");
39434             this.closeBtn.hide();
39435             this.hide();
39436         }else{
39437             if(!this.isVisible()){
39438                 this.show();
39439             }
39440         }
39441     },
39442
39443     /**
39444      * Adds the passed ContentPanel(s) to this region.
39445      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39446      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39447      */
39448     add : function(panel)
39449     {
39450         if(arguments.length > 1){
39451             for(var i = 0, len = arguments.length; i < len; i++) {
39452                 this.add(arguments[i]);
39453             }
39454             return null;
39455         }
39456         
39457         // if we have not been rendered yet, then we can not really do much of this..
39458         if (!this.bodyEl) {
39459             this.unrendered_panels.push(panel);
39460             return panel;
39461         }
39462         
39463         
39464         
39465         
39466         if(this.hasPanel(panel)){
39467             this.showPanel(panel);
39468             return panel;
39469         }
39470         panel.setRegion(this);
39471         this.panels.add(panel);
39472        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39473             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39474             // and hide them... ???
39475             this.bodyEl.dom.appendChild(panel.getEl().dom);
39476             if(panel.background !== true){
39477                 this.setActivePanel(panel);
39478             }
39479             this.fireEvent("paneladded", this, panel);
39480             return panel;
39481         }
39482         */
39483         if(!this.tabs){
39484             this.initTabs();
39485         }else{
39486             this.initPanelAsTab(panel);
39487         }
39488         
39489         
39490         if(panel.background !== true){
39491             this.tabs.activate(panel.getEl().id);
39492         }
39493         this.fireEvent("paneladded", this, panel);
39494         return panel;
39495     },
39496
39497     /**
39498      * Hides the tab for the specified panel.
39499      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39500      */
39501     hidePanel : function(panel){
39502         if(this.tabs && (panel = this.getPanel(panel))){
39503             this.tabs.hideTab(panel.getEl().id);
39504         }
39505     },
39506
39507     /**
39508      * Unhides the tab for a previously hidden panel.
39509      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39510      */
39511     unhidePanel : function(panel){
39512         if(this.tabs && (panel = this.getPanel(panel))){
39513             this.tabs.unhideTab(panel.getEl().id);
39514         }
39515     },
39516
39517     clearPanels : function(){
39518         while(this.panels.getCount() > 0){
39519              this.remove(this.panels.first());
39520         }
39521     },
39522
39523     /**
39524      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39525      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39526      * @param {Boolean} preservePanel Overrides the config preservePanel option
39527      * @return {Roo.ContentPanel} The panel that was removed
39528      */
39529     remove : function(panel, preservePanel)
39530     {
39531         panel = this.getPanel(panel);
39532         if(!panel){
39533             return null;
39534         }
39535         var e = {};
39536         this.fireEvent("beforeremove", this, panel, e);
39537         if(e.cancel === true){
39538             return null;
39539         }
39540         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39541         var panelId = panel.getId();
39542         this.panels.removeKey(panelId);
39543         if(preservePanel){
39544             document.body.appendChild(panel.getEl().dom);
39545         }
39546         if(this.tabs){
39547             this.tabs.removeTab(panel.getEl().id);
39548         }else if (!preservePanel){
39549             this.bodyEl.dom.removeChild(panel.getEl().dom);
39550         }
39551         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39552             var p = this.panels.first();
39553             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39554             tempEl.appendChild(p.getEl().dom);
39555             this.bodyEl.update("");
39556             this.bodyEl.dom.appendChild(p.getEl().dom);
39557             tempEl = null;
39558             this.updateTitle(p.getTitle());
39559             this.tabs = null;
39560             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39561             this.setActivePanel(p);
39562         }
39563         panel.setRegion(null);
39564         if(this.activePanel == panel){
39565             this.activePanel = null;
39566         }
39567         if(this.config.autoDestroy !== false && preservePanel !== true){
39568             try{panel.destroy();}catch(e){}
39569         }
39570         this.fireEvent("panelremoved", this, panel);
39571         return panel;
39572     },
39573
39574     /**
39575      * Returns the TabPanel component used by this region
39576      * @return {Roo.TabPanel}
39577      */
39578     getTabs : function(){
39579         return this.tabs;
39580     },
39581
39582     createTool : function(parentEl, className){
39583         var btn = Roo.DomHelper.append(parentEl, {
39584             tag: "div",
39585             cls: "x-layout-tools-button",
39586             children: [ {
39587                 tag: "div",
39588                 cls: "roo-layout-tools-button-inner " + className,
39589                 html: "&#160;"
39590             }]
39591         }, true);
39592         btn.addClassOnOver("roo-layout-tools-button-over");
39593         return btn;
39594     }
39595 });/*
39596  * Based on:
39597  * Ext JS Library 1.1.1
39598  * Copyright(c) 2006-2007, Ext JS, LLC.
39599  *
39600  * Originally Released Under LGPL - original licence link has changed is not relivant.
39601  *
39602  * Fork - LGPL
39603  * <script type="text/javascript">
39604  */
39605  
39606
39607
39608 /**
39609  * @class Roo.SplitLayoutRegion
39610  * @extends Roo.LayoutRegion
39611  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39612  */
39613 Roo.bootstrap.layout.Split = function(config){
39614     this.cursor = config.cursor;
39615     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39616 };
39617
39618 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39619 {
39620     splitTip : "Drag to resize.",
39621     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39622     useSplitTips : false,
39623
39624     applyConfig : function(config){
39625         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39626     },
39627     
39628     onRender : function(ctr,pos) {
39629         
39630         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39631         if(!this.config.split){
39632             return;
39633         }
39634         if(!this.split){
39635             
39636             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39637                             tag: "div",
39638                             id: this.el.id + "-split",
39639                             cls: "roo-layout-split roo-layout-split-"+this.position,
39640                             html: "&#160;"
39641             });
39642             /** The SplitBar for this region 
39643             * @type Roo.SplitBar */
39644             // does not exist yet...
39645             Roo.log([this.position, this.orientation]);
39646             
39647             this.split = new Roo.bootstrap.SplitBar({
39648                 dragElement : splitEl,
39649                 resizingElement: this.el,
39650                 orientation : this.orientation
39651             });
39652             
39653             this.split.on("moved", this.onSplitMove, this);
39654             this.split.useShim = this.config.useShim === true;
39655             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39656             if(this.useSplitTips){
39657                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39658             }
39659             //if(config.collapsible){
39660             //    this.split.el.on("dblclick", this.collapse,  this);
39661             //}
39662         }
39663         if(typeof this.config.minSize != "undefined"){
39664             this.split.minSize = this.config.minSize;
39665         }
39666         if(typeof this.config.maxSize != "undefined"){
39667             this.split.maxSize = this.config.maxSize;
39668         }
39669         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39670             this.hideSplitter();
39671         }
39672         
39673     },
39674
39675     getHMaxSize : function(){
39676          var cmax = this.config.maxSize || 10000;
39677          var center = this.mgr.getRegion("center");
39678          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39679     },
39680
39681     getVMaxSize : function(){
39682          var cmax = this.config.maxSize || 10000;
39683          var center = this.mgr.getRegion("center");
39684          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39685     },
39686
39687     onSplitMove : function(split, newSize){
39688         this.fireEvent("resized", this, newSize);
39689     },
39690     
39691     /** 
39692      * Returns the {@link Roo.SplitBar} for this region.
39693      * @return {Roo.SplitBar}
39694      */
39695     getSplitBar : function(){
39696         return this.split;
39697     },
39698     
39699     hide : function(){
39700         this.hideSplitter();
39701         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39702     },
39703
39704     hideSplitter : function(){
39705         if(this.split){
39706             this.split.el.setLocation(-2000,-2000);
39707             this.split.el.hide();
39708         }
39709     },
39710
39711     show : function(){
39712         if(this.split){
39713             this.split.el.show();
39714         }
39715         Roo.bootstrap.layout.Split.superclass.show.call(this);
39716     },
39717     
39718     beforeSlide: function(){
39719         if(Roo.isGecko){// firefox overflow auto bug workaround
39720             this.bodyEl.clip();
39721             if(this.tabs) {
39722                 this.tabs.bodyEl.clip();
39723             }
39724             if(this.activePanel){
39725                 this.activePanel.getEl().clip();
39726                 
39727                 if(this.activePanel.beforeSlide){
39728                     this.activePanel.beforeSlide();
39729                 }
39730             }
39731         }
39732     },
39733     
39734     afterSlide : function(){
39735         if(Roo.isGecko){// firefox overflow auto bug workaround
39736             this.bodyEl.unclip();
39737             if(this.tabs) {
39738                 this.tabs.bodyEl.unclip();
39739             }
39740             if(this.activePanel){
39741                 this.activePanel.getEl().unclip();
39742                 if(this.activePanel.afterSlide){
39743                     this.activePanel.afterSlide();
39744                 }
39745             }
39746         }
39747     },
39748
39749     initAutoHide : function(){
39750         if(this.autoHide !== false){
39751             if(!this.autoHideHd){
39752                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39753                 this.autoHideHd = {
39754                     "mouseout": function(e){
39755                         if(!e.within(this.el, true)){
39756                             st.delay(500);
39757                         }
39758                     },
39759                     "mouseover" : function(e){
39760                         st.cancel();
39761                     },
39762                     scope : this
39763                 };
39764             }
39765             this.el.on(this.autoHideHd);
39766         }
39767     },
39768
39769     clearAutoHide : function(){
39770         if(this.autoHide !== false){
39771             this.el.un("mouseout", this.autoHideHd.mouseout);
39772             this.el.un("mouseover", this.autoHideHd.mouseover);
39773         }
39774     },
39775
39776     clearMonitor : function(){
39777         Roo.get(document).un("click", this.slideInIf, this);
39778     },
39779
39780     // these names are backwards but not changed for compat
39781     slideOut : function(){
39782         if(this.isSlid || this.el.hasActiveFx()){
39783             return;
39784         }
39785         this.isSlid = true;
39786         if(this.collapseBtn){
39787             this.collapseBtn.hide();
39788         }
39789         this.closeBtnState = this.closeBtn.getStyle('display');
39790         this.closeBtn.hide();
39791         if(this.stickBtn){
39792             this.stickBtn.show();
39793         }
39794         this.el.show();
39795         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39796         this.beforeSlide();
39797         this.el.setStyle("z-index", 10001);
39798         this.el.slideIn(this.getSlideAnchor(), {
39799             callback: function(){
39800                 this.afterSlide();
39801                 this.initAutoHide();
39802                 Roo.get(document).on("click", this.slideInIf, this);
39803                 this.fireEvent("slideshow", this);
39804             },
39805             scope: this,
39806             block: true
39807         });
39808     },
39809
39810     afterSlideIn : function(){
39811         this.clearAutoHide();
39812         this.isSlid = false;
39813         this.clearMonitor();
39814         this.el.setStyle("z-index", "");
39815         if(this.collapseBtn){
39816             this.collapseBtn.show();
39817         }
39818         this.closeBtn.setStyle('display', this.closeBtnState);
39819         if(this.stickBtn){
39820             this.stickBtn.hide();
39821         }
39822         this.fireEvent("slidehide", this);
39823     },
39824
39825     slideIn : function(cb){
39826         if(!this.isSlid || this.el.hasActiveFx()){
39827             Roo.callback(cb);
39828             return;
39829         }
39830         this.isSlid = false;
39831         this.beforeSlide();
39832         this.el.slideOut(this.getSlideAnchor(), {
39833             callback: function(){
39834                 this.el.setLeftTop(-10000, -10000);
39835                 this.afterSlide();
39836                 this.afterSlideIn();
39837                 Roo.callback(cb);
39838             },
39839             scope: this,
39840             block: true
39841         });
39842     },
39843     
39844     slideInIf : function(e){
39845         if(!e.within(this.el)){
39846             this.slideIn();
39847         }
39848     },
39849
39850     animateCollapse : function(){
39851         this.beforeSlide();
39852         this.el.setStyle("z-index", 20000);
39853         var anchor = this.getSlideAnchor();
39854         this.el.slideOut(anchor, {
39855             callback : function(){
39856                 this.el.setStyle("z-index", "");
39857                 this.collapsedEl.slideIn(anchor, {duration:.3});
39858                 this.afterSlide();
39859                 this.el.setLocation(-10000,-10000);
39860                 this.el.hide();
39861                 this.fireEvent("collapsed", this);
39862             },
39863             scope: this,
39864             block: true
39865         });
39866     },
39867
39868     animateExpand : function(){
39869         this.beforeSlide();
39870         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39871         this.el.setStyle("z-index", 20000);
39872         this.collapsedEl.hide({
39873             duration:.1
39874         });
39875         this.el.slideIn(this.getSlideAnchor(), {
39876             callback : function(){
39877                 this.el.setStyle("z-index", "");
39878                 this.afterSlide();
39879                 if(this.split){
39880                     this.split.el.show();
39881                 }
39882                 this.fireEvent("invalidated", this);
39883                 this.fireEvent("expanded", this);
39884             },
39885             scope: this,
39886             block: true
39887         });
39888     },
39889
39890     anchors : {
39891         "west" : "left",
39892         "east" : "right",
39893         "north" : "top",
39894         "south" : "bottom"
39895     },
39896
39897     sanchors : {
39898         "west" : "l",
39899         "east" : "r",
39900         "north" : "t",
39901         "south" : "b"
39902     },
39903
39904     canchors : {
39905         "west" : "tl-tr",
39906         "east" : "tr-tl",
39907         "north" : "tl-bl",
39908         "south" : "bl-tl"
39909     },
39910
39911     getAnchor : function(){
39912         return this.anchors[this.position];
39913     },
39914
39915     getCollapseAnchor : function(){
39916         return this.canchors[this.position];
39917     },
39918
39919     getSlideAnchor : function(){
39920         return this.sanchors[this.position];
39921     },
39922
39923     getAlignAdj : function(){
39924         var cm = this.cmargins;
39925         switch(this.position){
39926             case "west":
39927                 return [0, 0];
39928             break;
39929             case "east":
39930                 return [0, 0];
39931             break;
39932             case "north":
39933                 return [0, 0];
39934             break;
39935             case "south":
39936                 return [0, 0];
39937             break;
39938         }
39939     },
39940
39941     getExpandAdj : function(){
39942         var c = this.collapsedEl, cm = this.cmargins;
39943         switch(this.position){
39944             case "west":
39945                 return [-(cm.right+c.getWidth()+cm.left), 0];
39946             break;
39947             case "east":
39948                 return [cm.right+c.getWidth()+cm.left, 0];
39949             break;
39950             case "north":
39951                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39952             break;
39953             case "south":
39954                 return [0, cm.top+cm.bottom+c.getHeight()];
39955             break;
39956         }
39957     }
39958 });/*
39959  * Based on:
39960  * Ext JS Library 1.1.1
39961  * Copyright(c) 2006-2007, Ext JS, LLC.
39962  *
39963  * Originally Released Under LGPL - original licence link has changed is not relivant.
39964  *
39965  * Fork - LGPL
39966  * <script type="text/javascript">
39967  */
39968 /*
39969  * These classes are private internal classes
39970  */
39971 Roo.bootstrap.layout.Center = function(config){
39972     config.region = "center";
39973     Roo.bootstrap.layout.Region.call(this, config);
39974     this.visible = true;
39975     this.minWidth = config.minWidth || 20;
39976     this.minHeight = config.minHeight || 20;
39977 };
39978
39979 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39980     hide : function(){
39981         // center panel can't be hidden
39982     },
39983     
39984     show : function(){
39985         // center panel can't be hidden
39986     },
39987     
39988     getMinWidth: function(){
39989         return this.minWidth;
39990     },
39991     
39992     getMinHeight: function(){
39993         return this.minHeight;
39994     }
39995 });
39996
39997
39998
39999
40000  
40001
40002
40003
40004
40005
40006
40007 Roo.bootstrap.layout.North = function(config)
40008 {
40009     config.region = 'north';
40010     config.cursor = 'n-resize';
40011     
40012     Roo.bootstrap.layout.Split.call(this, config);
40013     
40014     
40015     if(this.split){
40016         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40017         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40018         this.split.el.addClass("roo-layout-split-v");
40019     }
40020     //var size = config.initialSize || config.height;
40021     //if(this.el && typeof size != "undefined"){
40022     //    this.el.setHeight(size);
40023     //}
40024 };
40025 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40026 {
40027     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40028      
40029      
40030     onRender : function(ctr, pos)
40031     {
40032         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40033         var size = this.config.initialSize || this.config.height;
40034         if(this.el && typeof size != "undefined"){
40035             this.el.setHeight(size);
40036         }
40037     
40038     },
40039     
40040     getBox : function(){
40041         if(this.collapsed){
40042             return this.collapsedEl.getBox();
40043         }
40044         var box = this.el.getBox();
40045         if(this.split){
40046             box.height += this.split.el.getHeight();
40047         }
40048         return box;
40049     },
40050     
40051     updateBox : function(box){
40052         if(this.split && !this.collapsed){
40053             box.height -= this.split.el.getHeight();
40054             this.split.el.setLeft(box.x);
40055             this.split.el.setTop(box.y+box.height);
40056             this.split.el.setWidth(box.width);
40057         }
40058         if(this.collapsed){
40059             this.updateBody(box.width, null);
40060         }
40061         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40062     }
40063 });
40064
40065
40066
40067
40068
40069 Roo.bootstrap.layout.South = function(config){
40070     config.region = 'south';
40071     config.cursor = 's-resize';
40072     Roo.bootstrap.layout.Split.call(this, config);
40073     if(this.split){
40074         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40075         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40076         this.split.el.addClass("roo-layout-split-v");
40077     }
40078     
40079 };
40080
40081 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40082     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40083     
40084     onRender : function(ctr, pos)
40085     {
40086         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40087         var size = this.config.initialSize || this.config.height;
40088         if(this.el && typeof size != "undefined"){
40089             this.el.setHeight(size);
40090         }
40091     
40092     },
40093     
40094     getBox : function(){
40095         if(this.collapsed){
40096             return this.collapsedEl.getBox();
40097         }
40098         var box = this.el.getBox();
40099         if(this.split){
40100             var sh = this.split.el.getHeight();
40101             box.height += sh;
40102             box.y -= sh;
40103         }
40104         return box;
40105     },
40106     
40107     updateBox : function(box){
40108         if(this.split && !this.collapsed){
40109             var sh = this.split.el.getHeight();
40110             box.height -= sh;
40111             box.y += sh;
40112             this.split.el.setLeft(box.x);
40113             this.split.el.setTop(box.y-sh);
40114             this.split.el.setWidth(box.width);
40115         }
40116         if(this.collapsed){
40117             this.updateBody(box.width, null);
40118         }
40119         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40120     }
40121 });
40122
40123 Roo.bootstrap.layout.East = function(config){
40124     config.region = "east";
40125     config.cursor = "e-resize";
40126     Roo.bootstrap.layout.Split.call(this, config);
40127     if(this.split){
40128         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40129         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40130         this.split.el.addClass("roo-layout-split-h");
40131     }
40132     
40133 };
40134 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40135     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40136     
40137     onRender : function(ctr, pos)
40138     {
40139         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40140         var size = this.config.initialSize || this.config.width;
40141         if(this.el && typeof size != "undefined"){
40142             this.el.setWidth(size);
40143         }
40144     
40145     },
40146     
40147     getBox : function(){
40148         if(this.collapsed){
40149             return this.collapsedEl.getBox();
40150         }
40151         var box = this.el.getBox();
40152         if(this.split){
40153             var sw = this.split.el.getWidth();
40154             box.width += sw;
40155             box.x -= sw;
40156         }
40157         return box;
40158     },
40159
40160     updateBox : function(box){
40161         if(this.split && !this.collapsed){
40162             var sw = this.split.el.getWidth();
40163             box.width -= sw;
40164             this.split.el.setLeft(box.x);
40165             this.split.el.setTop(box.y);
40166             this.split.el.setHeight(box.height);
40167             box.x += sw;
40168         }
40169         if(this.collapsed){
40170             this.updateBody(null, box.height);
40171         }
40172         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40173     }
40174 });
40175
40176 Roo.bootstrap.layout.West = function(config){
40177     config.region = "west";
40178     config.cursor = "w-resize";
40179     
40180     Roo.bootstrap.layout.Split.call(this, config);
40181     if(this.split){
40182         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40183         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40184         this.split.el.addClass("roo-layout-split-h");
40185     }
40186     
40187 };
40188 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40189     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40190     
40191     onRender: function(ctr, pos)
40192     {
40193         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40194         var size = this.config.initialSize || this.config.width;
40195         if(typeof size != "undefined"){
40196             this.el.setWidth(size);
40197         }
40198     },
40199     
40200     getBox : function(){
40201         if(this.collapsed){
40202             return this.collapsedEl.getBox();
40203         }
40204         var box = this.el.getBox();
40205         if (box.width == 0) {
40206             box.width = this.config.width; // kludge?
40207         }
40208         if(this.split){
40209             box.width += this.split.el.getWidth();
40210         }
40211         return box;
40212     },
40213     
40214     updateBox : function(box){
40215         if(this.split && !this.collapsed){
40216             var sw = this.split.el.getWidth();
40217             box.width -= sw;
40218             this.split.el.setLeft(box.x+box.width);
40219             this.split.el.setTop(box.y);
40220             this.split.el.setHeight(box.height);
40221         }
40222         if(this.collapsed){
40223             this.updateBody(null, box.height);
40224         }
40225         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40226     }
40227 });/*
40228  * Based on:
40229  * Ext JS Library 1.1.1
40230  * Copyright(c) 2006-2007, Ext JS, LLC.
40231  *
40232  * Originally Released Under LGPL - original licence link has changed is not relivant.
40233  *
40234  * Fork - LGPL
40235  * <script type="text/javascript">
40236  */
40237 /**
40238  * @class Roo.bootstrap.paenl.Content
40239  * @extends Roo.util.Observable
40240  * @children Roo.bootstrap.Component
40241  * @parent builder Roo.bootstrap.layout.Border
40242  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40243  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40244  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40245  * @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
40246  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40247  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40248  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40249  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40250  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40251  * @cfg {String} title          The title for this panel
40252  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40253  * @cfg {String} url            Calls {@link #setUrl} with this value
40254  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40255  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40256  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40257  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40258  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40259  * @cfg {Boolean} badges render the badges
40260  * @cfg {String} cls  extra classes to use  
40261  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40262  
40263  * @constructor
40264  * Create a new ContentPanel.
40265  * @param {String/Object} config A string to set only the title or a config object
40266  
40267  */
40268 Roo.bootstrap.panel.Content = function( config){
40269     
40270     this.tpl = config.tpl || false;
40271     
40272     var el = config.el;
40273     var content = config.content;
40274
40275     if(config.autoCreate){ // xtype is available if this is called from factory
40276         el = Roo.id();
40277     }
40278     this.el = Roo.get(el);
40279     if(!this.el && config && config.autoCreate){
40280         if(typeof config.autoCreate == "object"){
40281             if(!config.autoCreate.id){
40282                 config.autoCreate.id = config.id||el;
40283             }
40284             this.el = Roo.DomHelper.append(document.body,
40285                         config.autoCreate, true);
40286         }else{
40287             var elcfg =  {
40288                 tag: "div",
40289                 cls: (config.cls || '') +
40290                     (config.background ? ' bg-' + config.background : '') +
40291                     " roo-layout-inactive-content",
40292                 id: config.id||el
40293             };
40294             if (config.iframe) {
40295                 elcfg.cn = [
40296                     {
40297                         tag : 'iframe',
40298                         style : 'border: 0px',
40299                         src : 'about:blank'
40300                     }
40301                 ];
40302             }
40303               
40304             if (config.html) {
40305                 elcfg.html = config.html;
40306                 
40307             }
40308                         
40309             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40310             if (config.iframe) {
40311                 this.iframeEl = this.el.select('iframe',true).first();
40312             }
40313             
40314         }
40315     } 
40316     this.closable = false;
40317     this.loaded = false;
40318     this.active = false;
40319    
40320       
40321     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40322         
40323         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40324         
40325         this.wrapEl = this.el; //this.el.wrap();
40326         var ti = [];
40327         if (config.toolbar.items) {
40328             ti = config.toolbar.items ;
40329             delete config.toolbar.items ;
40330         }
40331         
40332         var nitems = [];
40333         this.toolbar.render(this.wrapEl, 'before');
40334         for(var i =0;i < ti.length;i++) {
40335           //  Roo.log(['add child', items[i]]);
40336             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40337         }
40338         this.toolbar.items = nitems;
40339         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40340         delete config.toolbar;
40341         
40342     }
40343     /*
40344     // xtype created footer. - not sure if will work as we normally have to render first..
40345     if (this.footer && !this.footer.el && this.footer.xtype) {
40346         if (!this.wrapEl) {
40347             this.wrapEl = this.el.wrap();
40348         }
40349     
40350         this.footer.container = this.wrapEl.createChild();
40351          
40352         this.footer = Roo.factory(this.footer, Roo);
40353         
40354     }
40355     */
40356     
40357      if(typeof config == "string"){
40358         this.title = config;
40359     }else{
40360         Roo.apply(this, config);
40361     }
40362     
40363     if(this.resizeEl){
40364         this.resizeEl = Roo.get(this.resizeEl, true);
40365     }else{
40366         this.resizeEl = this.el;
40367     }
40368     // handle view.xtype
40369     
40370  
40371     
40372     
40373     this.addEvents({
40374         /**
40375          * @event activate
40376          * Fires when this panel is activated. 
40377          * @param {Roo.ContentPanel} this
40378          */
40379         "activate" : true,
40380         /**
40381          * @event deactivate
40382          * Fires when this panel is activated. 
40383          * @param {Roo.ContentPanel} this
40384          */
40385         "deactivate" : true,
40386
40387         /**
40388          * @event resize
40389          * Fires when this panel is resized if fitToFrame is true.
40390          * @param {Roo.ContentPanel} this
40391          * @param {Number} width The width after any component adjustments
40392          * @param {Number} height The height after any component adjustments
40393          */
40394         "resize" : true,
40395         
40396          /**
40397          * @event render
40398          * Fires when this tab is created
40399          * @param {Roo.ContentPanel} this
40400          */
40401         "render" : true,
40402         
40403           /**
40404          * @event scroll
40405          * Fires when this content is scrolled
40406          * @param {Roo.ContentPanel} this
40407          * @param {Event} scrollEvent
40408          */
40409         "scroll" : true
40410         
40411         
40412         
40413     });
40414     
40415
40416     
40417     
40418     if(this.autoScroll && !this.iframe){
40419         this.resizeEl.setStyle("overflow", "auto");
40420         this.resizeEl.on('scroll', this.onScroll, this);
40421     } else {
40422         // fix randome scrolling
40423         //this.el.on('scroll', function() {
40424         //    Roo.log('fix random scolling');
40425         //    this.scrollTo('top',0); 
40426         //});
40427     }
40428     content = content || this.content;
40429     if(content){
40430         this.setContent(content);
40431     }
40432     if(config && config.url){
40433         this.setUrl(this.url, this.params, this.loadOnce);
40434     }
40435     
40436     
40437     
40438     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40439     
40440     if (this.view && typeof(this.view.xtype) != 'undefined') {
40441         this.view.el = this.el.appendChild(document.createElement("div"));
40442         this.view = Roo.factory(this.view); 
40443         this.view.render  &&  this.view.render(false, '');  
40444     }
40445     
40446     
40447     this.fireEvent('render', this);
40448 };
40449
40450 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40451     
40452     cls : '',
40453     background : '',
40454     
40455     tabTip : '',
40456     
40457     iframe : false,
40458     iframeEl : false,
40459     
40460     /* Resize Element - use this to work out scroll etc. */
40461     resizeEl : false,
40462     
40463     setRegion : function(region){
40464         this.region = region;
40465         this.setActiveClass(region && !this.background);
40466     },
40467     
40468     
40469     setActiveClass: function(state)
40470     {
40471         if(state){
40472            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40473            this.el.setStyle('position','relative');
40474         }else{
40475            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40476            this.el.setStyle('position', 'absolute');
40477         } 
40478     },
40479     
40480     /**
40481      * Returns the toolbar for this Panel if one was configured. 
40482      * @return {Roo.Toolbar} 
40483      */
40484     getToolbar : function(){
40485         return this.toolbar;
40486     },
40487     
40488     setActiveState : function(active)
40489     {
40490         this.active = active;
40491         this.setActiveClass(active);
40492         if(!active){
40493             if(this.fireEvent("deactivate", this) === false){
40494                 return false;
40495             }
40496             return true;
40497         }
40498         this.fireEvent("activate", this);
40499         return true;
40500     },
40501     /**
40502      * Updates this panel's element (not for iframe)
40503      * @param {String} content The new content
40504      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40505     */
40506     setContent : function(content, loadScripts){
40507         if (this.iframe) {
40508             return;
40509         }
40510         
40511         this.el.update(content, loadScripts);
40512     },
40513
40514     ignoreResize : function(w, h)
40515     {
40516         return false; // always resize?
40517         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40518             return true;
40519         }else{
40520             this.lastSize = {width: w, height: h};
40521             return false;
40522         }
40523     },
40524     /**
40525      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40526      * @return {Roo.UpdateManager} The UpdateManager
40527      */
40528     getUpdateManager : function(){
40529         if (this.iframe) {
40530             return false;
40531         }
40532         return this.el.getUpdateManager();
40533     },
40534      /**
40535      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40536      * Does not work with IFRAME contents
40537      * @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:
40538 <pre><code>
40539 panel.load({
40540     url: "your-url.php",
40541     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40542     callback: yourFunction,
40543     scope: yourObject, //(optional scope)
40544     discardUrl: false,
40545     nocache: false,
40546     text: "Loading...",
40547     timeout: 30,
40548     scripts: false
40549 });
40550 </code></pre>
40551      
40552      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40553      * 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.
40554      * @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}
40555      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40556      * @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.
40557      * @return {Roo.ContentPanel} this
40558      */
40559     load : function(){
40560         
40561         if (this.iframe) {
40562             return this;
40563         }
40564         
40565         var um = this.el.getUpdateManager();
40566         um.update.apply(um, arguments);
40567         return this;
40568     },
40569
40570
40571     /**
40572      * 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.
40573      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40574      * @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)
40575      * @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)
40576      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40577      */
40578     setUrl : function(url, params, loadOnce){
40579         if (this.iframe) {
40580             this.iframeEl.dom.src = url;
40581             return false;
40582         }
40583         
40584         if(this.refreshDelegate){
40585             this.removeListener("activate", this.refreshDelegate);
40586         }
40587         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40588         this.on("activate", this.refreshDelegate);
40589         return this.el.getUpdateManager();
40590     },
40591     
40592     _handleRefresh : function(url, params, loadOnce){
40593         if(!loadOnce || !this.loaded){
40594             var updater = this.el.getUpdateManager();
40595             updater.update(url, params, this._setLoaded.createDelegate(this));
40596         }
40597     },
40598     
40599     _setLoaded : function(){
40600         this.loaded = true;
40601     }, 
40602     
40603     /**
40604      * Returns this panel's id
40605      * @return {String} 
40606      */
40607     getId : function(){
40608         return this.el.id;
40609     },
40610     
40611     /** 
40612      * Returns this panel's element - used by regiosn to add.
40613      * @return {Roo.Element} 
40614      */
40615     getEl : function(){
40616         return this.wrapEl || this.el;
40617     },
40618     
40619    
40620     
40621     adjustForComponents : function(width, height)
40622     {
40623         //Roo.log('adjustForComponents ');
40624         if(this.resizeEl != this.el){
40625             width -= this.el.getFrameWidth('lr');
40626             height -= this.el.getFrameWidth('tb');
40627         }
40628         if(this.toolbar){
40629             var te = this.toolbar.getEl();
40630             te.setWidth(width);
40631             height -= te.getHeight();
40632         }
40633         if(this.footer){
40634             var te = this.footer.getEl();
40635             te.setWidth(width);
40636             height -= te.getHeight();
40637         }
40638         
40639         
40640         if(this.adjustments){
40641             width += this.adjustments[0];
40642             height += this.adjustments[1];
40643         }
40644         return {"width": width, "height": height};
40645     },
40646     
40647     setSize : function(width, height){
40648         if(this.fitToFrame && !this.ignoreResize(width, height)){
40649             if(this.fitContainer && this.resizeEl != this.el){
40650                 this.el.setSize(width, height);
40651             }
40652             var size = this.adjustForComponents(width, height);
40653             if (this.iframe) {
40654                 this.iframeEl.setSize(width,height);
40655             }
40656             
40657             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40658             this.fireEvent('resize', this, size.width, size.height);
40659             
40660             
40661         }
40662     },
40663     
40664     /**
40665      * Returns this panel's title
40666      * @return {String} 
40667      */
40668     getTitle : function(){
40669         
40670         if (typeof(this.title) != 'object') {
40671             return this.title;
40672         }
40673         
40674         var t = '';
40675         for (var k in this.title) {
40676             if (!this.title.hasOwnProperty(k)) {
40677                 continue;
40678             }
40679             
40680             if (k.indexOf('-') >= 0) {
40681                 var s = k.split('-');
40682                 for (var i = 0; i<s.length; i++) {
40683                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40684                 }
40685             } else {
40686                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40687             }
40688         }
40689         return t;
40690     },
40691     
40692     /**
40693      * Set this panel's title
40694      * @param {String} title
40695      */
40696     setTitle : function(title){
40697         this.title = title;
40698         if(this.region){
40699             this.region.updatePanelTitle(this, title);
40700         }
40701     },
40702     
40703     /**
40704      * Returns true is this panel was configured to be closable
40705      * @return {Boolean} 
40706      */
40707     isClosable : function(){
40708         return this.closable;
40709     },
40710     
40711     beforeSlide : function(){
40712         this.el.clip();
40713         this.resizeEl.clip();
40714     },
40715     
40716     afterSlide : function(){
40717         this.el.unclip();
40718         this.resizeEl.unclip();
40719     },
40720     
40721     /**
40722      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40723      *   Will fail silently if the {@link #setUrl} method has not been called.
40724      *   This does not activate the panel, just updates its content.
40725      */
40726     refresh : function(){
40727         if(this.refreshDelegate){
40728            this.loaded = false;
40729            this.refreshDelegate();
40730         }
40731     },
40732     
40733     /**
40734      * Destroys this panel
40735      */
40736     destroy : function(){
40737         this.el.removeAllListeners();
40738         var tempEl = document.createElement("span");
40739         tempEl.appendChild(this.el.dom);
40740         tempEl.innerHTML = "";
40741         this.el.remove();
40742         this.el = null;
40743     },
40744     
40745     /**
40746      * form - if the content panel contains a form - this is a reference to it.
40747      * @type {Roo.form.Form}
40748      */
40749     form : false,
40750     /**
40751      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40752      *    This contains a reference to it.
40753      * @type {Roo.View}
40754      */
40755     view : false,
40756     
40757       /**
40758      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40759      * <pre><code>
40760
40761 layout.addxtype({
40762        xtype : 'Form',
40763        items: [ .... ]
40764    }
40765 );
40766
40767 </code></pre>
40768      * @param {Object} cfg Xtype definition of item to add.
40769      */
40770     
40771     
40772     getChildContainer: function () {
40773         return this.getEl();
40774     },
40775     
40776     
40777     onScroll : function(e)
40778     {
40779         this.fireEvent('scroll', this, e);
40780     }
40781     
40782     
40783     /*
40784         var  ret = new Roo.factory(cfg);
40785         return ret;
40786         
40787         
40788         // add form..
40789         if (cfg.xtype.match(/^Form$/)) {
40790             
40791             var el;
40792             //if (this.footer) {
40793             //    el = this.footer.container.insertSibling(false, 'before');
40794             //} else {
40795                 el = this.el.createChild();
40796             //}
40797
40798             this.form = new  Roo.form.Form(cfg);
40799             
40800             
40801             if ( this.form.allItems.length) {
40802                 this.form.render(el.dom);
40803             }
40804             return this.form;
40805         }
40806         // should only have one of theses..
40807         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40808             // views.. should not be just added - used named prop 'view''
40809             
40810             cfg.el = this.el.appendChild(document.createElement("div"));
40811             // factory?
40812             
40813             var ret = new Roo.factory(cfg);
40814              
40815              ret.render && ret.render(false, ''); // render blank..
40816             this.view = ret;
40817             return ret;
40818         }
40819         return false;
40820     }
40821     \*/
40822 });
40823  
40824 /**
40825  * @class Roo.bootstrap.panel.Grid
40826  * @extends Roo.bootstrap.panel.Content
40827  * @constructor
40828  * Create a new GridPanel.
40829  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40830  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40831  * @param {Object} config A the config object
40832   
40833  */
40834
40835
40836
40837 Roo.bootstrap.panel.Grid = function(config)
40838 {
40839     
40840       
40841     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40842         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40843
40844     config.el = this.wrapper;
40845     //this.el = this.wrapper;
40846     
40847       if (config.container) {
40848         // ctor'ed from a Border/panel.grid
40849         
40850         
40851         this.wrapper.setStyle("overflow", "hidden");
40852         this.wrapper.addClass('roo-grid-container');
40853
40854     }
40855     
40856     
40857     if(config.toolbar){
40858         var tool_el = this.wrapper.createChild();    
40859         this.toolbar = Roo.factory(config.toolbar);
40860         var ti = [];
40861         if (config.toolbar.items) {
40862             ti = config.toolbar.items ;
40863             delete config.toolbar.items ;
40864         }
40865         
40866         var nitems = [];
40867         this.toolbar.render(tool_el);
40868         for(var i =0;i < ti.length;i++) {
40869           //  Roo.log(['add child', items[i]]);
40870             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40871         }
40872         this.toolbar.items = nitems;
40873         
40874         delete config.toolbar;
40875     }
40876     
40877     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40878     config.grid.scrollBody = true;;
40879     config.grid.monitorWindowResize = false; // turn off autosizing
40880     config.grid.autoHeight = false;
40881     config.grid.autoWidth = false;
40882     
40883     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40884     
40885     if (config.background) {
40886         // render grid on panel activation (if panel background)
40887         this.on('activate', function(gp) {
40888             if (!gp.grid.rendered) {
40889                 gp.grid.render(this.wrapper);
40890                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40891             }
40892         });
40893             
40894     } else {
40895         this.grid.render(this.wrapper);
40896         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40897
40898     }
40899     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40900     // ??? needed ??? config.el = this.wrapper;
40901     
40902     
40903     
40904   
40905     // xtype created footer. - not sure if will work as we normally have to render first..
40906     if (this.footer && !this.footer.el && this.footer.xtype) {
40907         
40908         var ctr = this.grid.getView().getFooterPanel(true);
40909         this.footer.dataSource = this.grid.dataSource;
40910         this.footer = Roo.factory(this.footer, Roo);
40911         this.footer.render(ctr);
40912         
40913     }
40914     
40915     
40916     
40917     
40918      
40919 };
40920
40921 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40922 {
40923     // private
40924     is_resizing : false,
40925     
40926     getId : function(){
40927         return this.grid.id;
40928     },
40929     
40930     /**
40931      * Returns the grid for this panel
40932      * @return {Roo.bootstrap.Table} 
40933      */
40934     getGrid : function(){
40935         return this.grid;    
40936     },
40937     
40938     setSize : function(width, height)
40939     {
40940         if (this.is_resizing) {
40941             return;
40942         
40943         }
40944         this.is_resizing = true;
40945         if(!this.ignoreResize(width, height)){
40946             var grid = this.grid;
40947             var size = this.adjustForComponents(width, height);
40948             // tfoot is not a footer?
40949           
40950             
40951             var gridel = grid.getGridEl();
40952             gridel.setSize(size.width, size.height);
40953             
40954             var tbd = grid.getGridEl().select('tbody', true).first();
40955             var thd = grid.getGridEl().select('thead',true).first();
40956             var tbf= grid.getGridEl().select('tfoot', true).first();
40957
40958             if (tbf) {
40959                 size.height -= tbf.getHeight();
40960             }
40961             if (thd) {
40962                 size.height -= thd.getHeight();
40963             }
40964             
40965             tbd.setSize(size.width, size.height );
40966             // this is for the account management tab -seems to work there.
40967             var thd = grid.getGridEl().select('thead',true).first();
40968             //if (tbd) {
40969             //    tbd.setSize(size.width, size.height - thd.getHeight());
40970             //}
40971              
40972             grid.autoSize();
40973         }
40974         this.is_resizing = false;
40975     },
40976      
40977     
40978     
40979     beforeSlide : function(){
40980         this.grid.getView().scroller.clip();
40981     },
40982     
40983     afterSlide : function(){
40984         this.grid.getView().scroller.unclip();
40985     },
40986     
40987     destroy : function(){
40988         this.grid.destroy();
40989         delete this.grid;
40990         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40991     }
40992 });
40993
40994 /**
40995  * @class Roo.bootstrap.panel.Nest
40996  * @extends Roo.bootstrap.panel.Content
40997  * @constructor
40998  * Create a new Panel, that can contain a layout.Border.
40999  * 
41000  * 
41001  * @param {String/Object} config A string to set only the title or a config object
41002  */
41003 Roo.bootstrap.panel.Nest = function(config)
41004 {
41005     // construct with only one argument..
41006     /* FIXME - implement nicer consturctors
41007     if (layout.layout) {
41008         config = layout;
41009         layout = config.layout;
41010         delete config.layout;
41011     }
41012     if (layout.xtype && !layout.getEl) {
41013         // then layout needs constructing..
41014         layout = Roo.factory(layout, Roo);
41015     }
41016     */
41017     
41018     config.el =  config.layout.getEl();
41019     
41020     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41021     
41022     config.layout.monitorWindowResize = false; // turn off autosizing
41023     this.layout = config.layout;
41024     this.layout.getEl().addClass("roo-layout-nested-layout");
41025     this.layout.parent = this;
41026     
41027     
41028     
41029     
41030 };
41031
41032 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41033     /**
41034     * @cfg {Roo.BorderLayout} layout The layout for this panel
41035     */
41036     layout : false,
41037
41038     setSize : function(width, height){
41039         if(!this.ignoreResize(width, height)){
41040             var size = this.adjustForComponents(width, height);
41041             var el = this.layout.getEl();
41042             if (size.height < 1) {
41043                 el.setWidth(size.width);   
41044             } else {
41045                 el.setSize(size.width, size.height);
41046             }
41047             var touch = el.dom.offsetWidth;
41048             this.layout.layout();
41049             // ie requires a double layout on the first pass
41050             if(Roo.isIE && !this.initialized){
41051                 this.initialized = true;
41052                 this.layout.layout();
41053             }
41054         }
41055     },
41056     
41057     // activate all subpanels if not currently active..
41058     
41059     setActiveState : function(active){
41060         this.active = active;
41061         this.setActiveClass(active);
41062         
41063         if(!active){
41064             this.fireEvent("deactivate", this);
41065             return;
41066         }
41067         
41068         this.fireEvent("activate", this);
41069         // not sure if this should happen before or after..
41070         if (!this.layout) {
41071             return; // should not happen..
41072         }
41073         var reg = false;
41074         for (var r in this.layout.regions) {
41075             reg = this.layout.getRegion(r);
41076             if (reg.getActivePanel()) {
41077                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41078                 reg.setActivePanel(reg.getActivePanel());
41079                 continue;
41080             }
41081             if (!reg.panels.length) {
41082                 continue;
41083             }
41084             reg.showPanel(reg.getPanel(0));
41085         }
41086         
41087         
41088         
41089         
41090     },
41091     
41092     /**
41093      * Returns the nested BorderLayout for this panel
41094      * @return {Roo.BorderLayout} 
41095      */
41096     getLayout : function(){
41097         return this.layout;
41098     },
41099     
41100      /**
41101      * Adds a xtype elements to the layout of the nested panel
41102      * <pre><code>
41103
41104 panel.addxtype({
41105        xtype : 'ContentPanel',
41106        region: 'west',
41107        items: [ .... ]
41108    }
41109 );
41110
41111 panel.addxtype({
41112         xtype : 'NestedLayoutPanel',
41113         region: 'west',
41114         layout: {
41115            center: { },
41116            west: { }   
41117         },
41118         items : [ ... list of content panels or nested layout panels.. ]
41119    }
41120 );
41121 </code></pre>
41122      * @param {Object} cfg Xtype definition of item to add.
41123      */
41124     addxtype : function(cfg) {
41125         return this.layout.addxtype(cfg);
41126     
41127     }
41128 });/*
41129  * Based on:
41130  * Ext JS Library 1.1.1
41131  * Copyright(c) 2006-2007, Ext JS, LLC.
41132  *
41133  * Originally Released Under LGPL - original licence link has changed is not relivant.
41134  *
41135  * Fork - LGPL
41136  * <script type="text/javascript">
41137  */
41138 /**
41139  * @class Roo.TabPanel
41140  * @extends Roo.util.Observable
41141  * A lightweight tab container.
41142  * <br><br>
41143  * Usage:
41144  * <pre><code>
41145 // basic tabs 1, built from existing content
41146 var tabs = new Roo.TabPanel("tabs1");
41147 tabs.addTab("script", "View Script");
41148 tabs.addTab("markup", "View Markup");
41149 tabs.activate("script");
41150
41151 // more advanced tabs, built from javascript
41152 var jtabs = new Roo.TabPanel("jtabs");
41153 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41154
41155 // set up the UpdateManager
41156 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41157 var updater = tab2.getUpdateManager();
41158 updater.setDefaultUrl("ajax1.htm");
41159 tab2.on('activate', updater.refresh, updater, true);
41160
41161 // Use setUrl for Ajax loading
41162 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41163 tab3.setUrl("ajax2.htm", null, true);
41164
41165 // Disabled tab
41166 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41167 tab4.disable();
41168
41169 jtabs.activate("jtabs-1");
41170  * </code></pre>
41171  * @constructor
41172  * Create a new TabPanel.
41173  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41174  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41175  */
41176 Roo.bootstrap.panel.Tabs = function(config){
41177     /**
41178     * The container element for this TabPanel.
41179     * @type Roo.Element
41180     */
41181     this.el = Roo.get(config.el);
41182     delete config.el;
41183     if(config){
41184         if(typeof config == "boolean"){
41185             this.tabPosition = config ? "bottom" : "top";
41186         }else{
41187             Roo.apply(this, config);
41188         }
41189     }
41190     
41191     if(this.tabPosition == "bottom"){
41192         // if tabs are at the bottom = create the body first.
41193         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41194         this.el.addClass("roo-tabs-bottom");
41195     }
41196     // next create the tabs holders
41197     
41198     if (this.tabPosition == "west"){
41199         
41200         var reg = this.region; // fake it..
41201         while (reg) {
41202             if (!reg.mgr.parent) {
41203                 break;
41204             }
41205             reg = reg.mgr.parent.region;
41206         }
41207         Roo.log("got nest?");
41208         Roo.log(reg);
41209         if (reg.mgr.getRegion('west')) {
41210             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41211             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41212             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41213             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41214             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41215         
41216             
41217         }
41218         
41219         
41220     } else {
41221      
41222         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41223         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41224         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41225         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41226     }
41227     
41228     
41229     if(Roo.isIE){
41230         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41231     }
41232     
41233     // finally - if tabs are at the top, then create the body last..
41234     if(this.tabPosition != "bottom"){
41235         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41236          * @type Roo.Element
41237          */
41238         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41239         this.el.addClass("roo-tabs-top");
41240     }
41241     this.items = [];
41242
41243     this.bodyEl.setStyle("position", "relative");
41244
41245     this.active = null;
41246     this.activateDelegate = this.activate.createDelegate(this);
41247
41248     this.addEvents({
41249         /**
41250          * @event tabchange
41251          * Fires when the active tab changes
41252          * @param {Roo.TabPanel} this
41253          * @param {Roo.TabPanelItem} activePanel The new active tab
41254          */
41255         "tabchange": true,
41256         /**
41257          * @event beforetabchange
41258          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41259          * @param {Roo.TabPanel} this
41260          * @param {Object} e Set cancel to true on this object to cancel the tab change
41261          * @param {Roo.TabPanelItem} tab The tab being changed to
41262          */
41263         "beforetabchange" : true
41264     });
41265
41266     Roo.EventManager.onWindowResize(this.onResize, this);
41267     this.cpad = this.el.getPadding("lr");
41268     this.hiddenCount = 0;
41269
41270
41271     // toolbar on the tabbar support...
41272     if (this.toolbar) {
41273         alert("no toolbar support yet");
41274         this.toolbar  = false;
41275         /*
41276         var tcfg = this.toolbar;
41277         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41278         this.toolbar = new Roo.Toolbar(tcfg);
41279         if (Roo.isSafari) {
41280             var tbl = tcfg.container.child('table', true);
41281             tbl.setAttribute('width', '100%');
41282         }
41283         */
41284         
41285     }
41286    
41287
41288
41289     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41290 };
41291
41292 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41293     /*
41294      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41295      */
41296     tabPosition : "top",
41297     /*
41298      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41299      */
41300     currentTabWidth : 0,
41301     /*
41302      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41303      */
41304     minTabWidth : 40,
41305     /*
41306      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41307      */
41308     maxTabWidth : 250,
41309     /*
41310      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41311      */
41312     preferredTabWidth : 175,
41313     /*
41314      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41315      */
41316     resizeTabs : false,
41317     /*
41318      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41319      */
41320     monitorResize : true,
41321     /*
41322      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41323      */
41324     toolbar : false,  // set by caller..
41325     
41326     region : false, /// set by caller
41327     
41328     disableTooltips : true, // not used yet...
41329
41330     /**
41331      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41332      * @param {String} id The id of the div to use <b>or create</b>
41333      * @param {String} text The text for the tab
41334      * @param {String} content (optional) Content to put in the TabPanelItem body
41335      * @param {Boolean} closable (optional) True to create a close icon on the tab
41336      * @return {Roo.TabPanelItem} The created TabPanelItem
41337      */
41338     addTab : function(id, text, content, closable, tpl)
41339     {
41340         var item = new Roo.bootstrap.panel.TabItem({
41341             panel: this,
41342             id : id,
41343             text : text,
41344             closable : closable,
41345             tpl : tpl
41346         });
41347         this.addTabItem(item);
41348         if(content){
41349             item.setContent(content);
41350         }
41351         return item;
41352     },
41353
41354     /**
41355      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41356      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41357      * @return {Roo.TabPanelItem}
41358      */
41359     getTab : function(id){
41360         return this.items[id];
41361     },
41362
41363     /**
41364      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41365      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41366      */
41367     hideTab : function(id){
41368         var t = this.items[id];
41369         if(!t.isHidden()){
41370            t.setHidden(true);
41371            this.hiddenCount++;
41372            this.autoSizeTabs();
41373         }
41374     },
41375
41376     /**
41377      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41378      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41379      */
41380     unhideTab : function(id){
41381         var t = this.items[id];
41382         if(t.isHidden()){
41383            t.setHidden(false);
41384            this.hiddenCount--;
41385            this.autoSizeTabs();
41386         }
41387     },
41388
41389     /**
41390      * Adds an existing {@link Roo.TabPanelItem}.
41391      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41392      */
41393     addTabItem : function(item)
41394     {
41395         this.items[item.id] = item;
41396         this.items.push(item);
41397         this.autoSizeTabs();
41398       //  if(this.resizeTabs){
41399     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41400   //         this.autoSizeTabs();
41401 //        }else{
41402 //            item.autoSize();
41403        // }
41404     },
41405
41406     /**
41407      * Removes a {@link Roo.TabPanelItem}.
41408      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41409      */
41410     removeTab : function(id){
41411         var items = this.items;
41412         var tab = items[id];
41413         if(!tab) { return; }
41414         var index = items.indexOf(tab);
41415         if(this.active == tab && items.length > 1){
41416             var newTab = this.getNextAvailable(index);
41417             if(newTab) {
41418                 newTab.activate();
41419             }
41420         }
41421         this.stripEl.dom.removeChild(tab.pnode.dom);
41422         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41423             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41424         }
41425         items.splice(index, 1);
41426         delete this.items[tab.id];
41427         tab.fireEvent("close", tab);
41428         tab.purgeListeners();
41429         this.autoSizeTabs();
41430     },
41431
41432     getNextAvailable : function(start){
41433         var items = this.items;
41434         var index = start;
41435         // look for a next tab that will slide over to
41436         // replace the one being removed
41437         while(index < items.length){
41438             var item = items[++index];
41439             if(item && !item.isHidden()){
41440                 return item;
41441             }
41442         }
41443         // if one isn't found select the previous tab (on the left)
41444         index = start;
41445         while(index >= 0){
41446             var item = items[--index];
41447             if(item && !item.isHidden()){
41448                 return item;
41449             }
41450         }
41451         return null;
41452     },
41453
41454     /**
41455      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41456      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41457      */
41458     disableTab : function(id){
41459         var tab = this.items[id];
41460         if(tab && this.active != tab){
41461             tab.disable();
41462         }
41463     },
41464
41465     /**
41466      * Enables a {@link Roo.TabPanelItem} that is disabled.
41467      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41468      */
41469     enableTab : function(id){
41470         var tab = this.items[id];
41471         tab.enable();
41472     },
41473
41474     /**
41475      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41476      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41477      * @return {Roo.TabPanelItem} The TabPanelItem.
41478      */
41479     activate : function(id)
41480     {
41481         //Roo.log('activite:'  + id);
41482         
41483         var tab = this.items[id];
41484         if(!tab){
41485             return null;
41486         }
41487         if(tab == this.active || tab.disabled){
41488             return tab;
41489         }
41490         var e = {};
41491         this.fireEvent("beforetabchange", this, e, tab);
41492         if(e.cancel !== true && !tab.disabled){
41493             if(this.active){
41494                 this.active.hide();
41495             }
41496             this.active = this.items[id];
41497             this.active.show();
41498             this.fireEvent("tabchange", this, this.active);
41499         }
41500         return tab;
41501     },
41502
41503     /**
41504      * Gets the active {@link Roo.TabPanelItem}.
41505      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41506      */
41507     getActiveTab : function(){
41508         return this.active;
41509     },
41510
41511     /**
41512      * Updates the tab body element to fit the height of the container element
41513      * for overflow scrolling
41514      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41515      */
41516     syncHeight : function(targetHeight){
41517         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41518         var bm = this.bodyEl.getMargins();
41519         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41520         this.bodyEl.setHeight(newHeight);
41521         return newHeight;
41522     },
41523
41524     onResize : function(){
41525         if(this.monitorResize){
41526             this.autoSizeTabs();
41527         }
41528     },
41529
41530     /**
41531      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41532      */
41533     beginUpdate : function(){
41534         this.updating = true;
41535     },
41536
41537     /**
41538      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41539      */
41540     endUpdate : function(){
41541         this.updating = false;
41542         this.autoSizeTabs();
41543     },
41544
41545     /**
41546      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41547      */
41548     autoSizeTabs : function()
41549     {
41550         var count = this.items.length;
41551         var vcount = count - this.hiddenCount;
41552         
41553         if (vcount < 2) {
41554             this.stripEl.hide();
41555         } else {
41556             this.stripEl.show();
41557         }
41558         
41559         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41560             return;
41561         }
41562         
41563         
41564         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41565         var availWidth = Math.floor(w / vcount);
41566         var b = this.stripBody;
41567         if(b.getWidth() > w){
41568             var tabs = this.items;
41569             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41570             if(availWidth < this.minTabWidth){
41571                 /*if(!this.sleft){    // incomplete scrolling code
41572                     this.createScrollButtons();
41573                 }
41574                 this.showScroll();
41575                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41576             }
41577         }else{
41578             if(this.currentTabWidth < this.preferredTabWidth){
41579                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41580             }
41581         }
41582     },
41583
41584     /**
41585      * Returns the number of tabs in this TabPanel.
41586      * @return {Number}
41587      */
41588      getCount : function(){
41589          return this.items.length;
41590      },
41591
41592     /**
41593      * Resizes all the tabs to the passed width
41594      * @param {Number} The new width
41595      */
41596     setTabWidth : function(width){
41597         this.currentTabWidth = width;
41598         for(var i = 0, len = this.items.length; i < len; i++) {
41599                 if(!this.items[i].isHidden()) {
41600                 this.items[i].setWidth(width);
41601             }
41602         }
41603     },
41604
41605     /**
41606      * Destroys this TabPanel
41607      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41608      */
41609     destroy : function(removeEl){
41610         Roo.EventManager.removeResizeListener(this.onResize, this);
41611         for(var i = 0, len = this.items.length; i < len; i++){
41612             this.items[i].purgeListeners();
41613         }
41614         if(removeEl === true){
41615             this.el.update("");
41616             this.el.remove();
41617         }
41618     },
41619     
41620     createStrip : function(container)
41621     {
41622         var strip = document.createElement("nav");
41623         strip.className = Roo.bootstrap.version == 4 ?
41624             "navbar-light bg-light" : 
41625             "navbar navbar-default"; //"x-tabs-wrap";
41626         container.appendChild(strip);
41627         return strip;
41628     },
41629     
41630     createStripList : function(strip)
41631     {
41632         // div wrapper for retard IE
41633         // returns the "tr" element.
41634         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41635         //'<div class="x-tabs-strip-wrap">'+
41636           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41637           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41638         return strip.firstChild; //.firstChild.firstChild.firstChild;
41639     },
41640     createBody : function(container)
41641     {
41642         var body = document.createElement("div");
41643         Roo.id(body, "tab-body");
41644         //Roo.fly(body).addClass("x-tabs-body");
41645         Roo.fly(body).addClass("tab-content");
41646         container.appendChild(body);
41647         return body;
41648     },
41649     createItemBody :function(bodyEl, id){
41650         var body = Roo.getDom(id);
41651         if(!body){
41652             body = document.createElement("div");
41653             body.id = id;
41654         }
41655         //Roo.fly(body).addClass("x-tabs-item-body");
41656         Roo.fly(body).addClass("tab-pane");
41657          bodyEl.insertBefore(body, bodyEl.firstChild);
41658         return body;
41659     },
41660     /** @private */
41661     createStripElements :  function(stripEl, text, closable, tpl)
41662     {
41663         var td = document.createElement("li"); // was td..
41664         td.className = 'nav-item';
41665         
41666         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41667         
41668         
41669         stripEl.appendChild(td);
41670         /*if(closable){
41671             td.className = "x-tabs-closable";
41672             if(!this.closeTpl){
41673                 this.closeTpl = new Roo.Template(
41674                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41675                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41676                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41677                 );
41678             }
41679             var el = this.closeTpl.overwrite(td, {"text": text});
41680             var close = el.getElementsByTagName("div")[0];
41681             var inner = el.getElementsByTagName("em")[0];
41682             return {"el": el, "close": close, "inner": inner};
41683         } else {
41684         */
41685         // not sure what this is..
41686 //            if(!this.tabTpl){
41687                 //this.tabTpl = new Roo.Template(
41688                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41689                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41690                 //);
41691 //                this.tabTpl = new Roo.Template(
41692 //                   '<a href="#">' +
41693 //                   '<span unselectable="on"' +
41694 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41695 //                            ' >{text}</span></a>'
41696 //                );
41697 //                
41698 //            }
41699
41700
41701             var template = tpl || this.tabTpl || false;
41702             
41703             if(!template){
41704                 template =  new Roo.Template(
41705                         Roo.bootstrap.version == 4 ? 
41706                             (
41707                                 '<a class="nav-link" href="#" unselectable="on"' +
41708                                      (this.disableTooltips ? '' : ' title="{text}"') +
41709                                      ' >{text}</a>'
41710                             ) : (
41711                                 '<a class="nav-link" href="#">' +
41712                                 '<span unselectable="on"' +
41713                                          (this.disableTooltips ? '' : ' title="{text}"') +
41714                                     ' >{text}</span></a>'
41715                             )
41716                 );
41717             }
41718             
41719             switch (typeof(template)) {
41720                 case 'object' :
41721                     break;
41722                 case 'string' :
41723                     template = new Roo.Template(template);
41724                     break;
41725                 default :
41726                     break;
41727             }
41728             
41729             var el = template.overwrite(td, {"text": text});
41730             
41731             var inner = el.getElementsByTagName("span")[0];
41732             
41733             return {"el": el, "inner": inner};
41734             
41735     }
41736         
41737     
41738 });
41739
41740 /**
41741  * @class Roo.TabPanelItem
41742  * @extends Roo.util.Observable
41743  * Represents an individual item (tab plus body) in a TabPanel.
41744  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41745  * @param {String} id The id of this TabPanelItem
41746  * @param {String} text The text for the tab of this TabPanelItem
41747  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41748  */
41749 Roo.bootstrap.panel.TabItem = function(config){
41750     /**
41751      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41752      * @type Roo.TabPanel
41753      */
41754     this.tabPanel = config.panel;
41755     /**
41756      * The id for this TabPanelItem
41757      * @type String
41758      */
41759     this.id = config.id;
41760     /** @private */
41761     this.disabled = false;
41762     /** @private */
41763     this.text = config.text;
41764     /** @private */
41765     this.loaded = false;
41766     this.closable = config.closable;
41767
41768     /**
41769      * The body element for this TabPanelItem.
41770      * @type Roo.Element
41771      */
41772     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41773     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41774     this.bodyEl.setStyle("display", "block");
41775     this.bodyEl.setStyle("zoom", "1");
41776     //this.hideAction();
41777
41778     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41779     /** @private */
41780     this.el = Roo.get(els.el);
41781     this.inner = Roo.get(els.inner, true);
41782      this.textEl = Roo.bootstrap.version == 4 ?
41783         this.el : Roo.get(this.el.dom.firstChild, true);
41784
41785     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41786     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41787
41788     
41789 //    this.el.on("mousedown", this.onTabMouseDown, this);
41790     this.el.on("click", this.onTabClick, this);
41791     /** @private */
41792     if(config.closable){
41793         var c = Roo.get(els.close, true);
41794         c.dom.title = this.closeText;
41795         c.addClassOnOver("close-over");
41796         c.on("click", this.closeClick, this);
41797      }
41798
41799     this.addEvents({
41800          /**
41801          * @event activate
41802          * Fires when this tab becomes the active tab.
41803          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41804          * @param {Roo.TabPanelItem} this
41805          */
41806         "activate": true,
41807         /**
41808          * @event beforeclose
41809          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41810          * @param {Roo.TabPanelItem} this
41811          * @param {Object} e Set cancel to true on this object to cancel the close.
41812          */
41813         "beforeclose": true,
41814         /**
41815          * @event close
41816          * Fires when this tab is closed.
41817          * @param {Roo.TabPanelItem} this
41818          */
41819          "close": true,
41820         /**
41821          * @event deactivate
41822          * Fires when this tab is no longer the active tab.
41823          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41824          * @param {Roo.TabPanelItem} this
41825          */
41826          "deactivate" : true
41827     });
41828     this.hidden = false;
41829
41830     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41831 };
41832
41833 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41834            {
41835     purgeListeners : function(){
41836        Roo.util.Observable.prototype.purgeListeners.call(this);
41837        this.el.removeAllListeners();
41838     },
41839     /**
41840      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41841      */
41842     show : function(){
41843         this.status_node.addClass("active");
41844         this.showAction();
41845         if(Roo.isOpera){
41846             this.tabPanel.stripWrap.repaint();
41847         }
41848         this.fireEvent("activate", this.tabPanel, this);
41849     },
41850
41851     /**
41852      * Returns true if this tab is the active tab.
41853      * @return {Boolean}
41854      */
41855     isActive : function(){
41856         return this.tabPanel.getActiveTab() == this;
41857     },
41858
41859     /**
41860      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41861      */
41862     hide : function(){
41863         this.status_node.removeClass("active");
41864         this.hideAction();
41865         this.fireEvent("deactivate", this.tabPanel, this);
41866     },
41867
41868     hideAction : function(){
41869         this.bodyEl.hide();
41870         this.bodyEl.setStyle("position", "absolute");
41871         this.bodyEl.setLeft("-20000px");
41872         this.bodyEl.setTop("-20000px");
41873     },
41874
41875     showAction : function(){
41876         this.bodyEl.setStyle("position", "relative");
41877         this.bodyEl.setTop("");
41878         this.bodyEl.setLeft("");
41879         this.bodyEl.show();
41880     },
41881
41882     /**
41883      * Set the tooltip for the tab.
41884      * @param {String} tooltip The tab's tooltip
41885      */
41886     setTooltip : function(text){
41887         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41888             this.textEl.dom.qtip = text;
41889             this.textEl.dom.removeAttribute('title');
41890         }else{
41891             this.textEl.dom.title = text;
41892         }
41893     },
41894
41895     onTabClick : function(e){
41896         e.preventDefault();
41897         this.tabPanel.activate(this.id);
41898     },
41899
41900     onTabMouseDown : function(e){
41901         e.preventDefault();
41902         this.tabPanel.activate(this.id);
41903     },
41904 /*
41905     getWidth : function(){
41906         return this.inner.getWidth();
41907     },
41908
41909     setWidth : function(width){
41910         var iwidth = width - this.linode.getPadding("lr");
41911         this.inner.setWidth(iwidth);
41912         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41913         this.linode.setWidth(width);
41914     },
41915 */
41916     /**
41917      * Show or hide the tab
41918      * @param {Boolean} hidden True to hide or false to show.
41919      */
41920     setHidden : function(hidden){
41921         this.hidden = hidden;
41922         this.linode.setStyle("display", hidden ? "none" : "");
41923     },
41924
41925     /**
41926      * Returns true if this tab is "hidden"
41927      * @return {Boolean}
41928      */
41929     isHidden : function(){
41930         return this.hidden;
41931     },
41932
41933     /**
41934      * Returns the text for this tab
41935      * @return {String}
41936      */
41937     getText : function(){
41938         return this.text;
41939     },
41940     /*
41941     autoSize : function(){
41942         //this.el.beginMeasure();
41943         this.textEl.setWidth(1);
41944         /*
41945          *  #2804 [new] Tabs in Roojs
41946          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41947          */
41948         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41949         //this.el.endMeasure();
41950     //},
41951
41952     /**
41953      * Sets the text for the tab (Note: this also sets the tooltip text)
41954      * @param {String} text The tab's text and tooltip
41955      */
41956     setText : function(text){
41957         this.text = text;
41958         this.textEl.update(text);
41959         this.setTooltip(text);
41960         //if(!this.tabPanel.resizeTabs){
41961         //    this.autoSize();
41962         //}
41963     },
41964     /**
41965      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41966      */
41967     activate : function(){
41968         this.tabPanel.activate(this.id);
41969     },
41970
41971     /**
41972      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41973      */
41974     disable : function(){
41975         if(this.tabPanel.active != this){
41976             this.disabled = true;
41977             this.status_node.addClass("disabled");
41978         }
41979     },
41980
41981     /**
41982      * Enables this TabPanelItem if it was previously disabled.
41983      */
41984     enable : function(){
41985         this.disabled = false;
41986         this.status_node.removeClass("disabled");
41987     },
41988
41989     /**
41990      * Sets the content for this TabPanelItem.
41991      * @param {String} content The content
41992      * @param {Boolean} loadScripts true to look for and load scripts
41993      */
41994     setContent : function(content, loadScripts){
41995         this.bodyEl.update(content, loadScripts);
41996     },
41997
41998     /**
41999      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42000      * @return {Roo.UpdateManager} The UpdateManager
42001      */
42002     getUpdateManager : function(){
42003         return this.bodyEl.getUpdateManager();
42004     },
42005
42006     /**
42007      * Set a URL to be used to load the content for this TabPanelItem.
42008      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42009      * @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)
42010      * @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)
42011      * @return {Roo.UpdateManager} The UpdateManager
42012      */
42013     setUrl : function(url, params, loadOnce){
42014         if(this.refreshDelegate){
42015             this.un('activate', this.refreshDelegate);
42016         }
42017         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42018         this.on("activate", this.refreshDelegate);
42019         return this.bodyEl.getUpdateManager();
42020     },
42021
42022     /** @private */
42023     _handleRefresh : function(url, params, loadOnce){
42024         if(!loadOnce || !this.loaded){
42025             var updater = this.bodyEl.getUpdateManager();
42026             updater.update(url, params, this._setLoaded.createDelegate(this));
42027         }
42028     },
42029
42030     /**
42031      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42032      *   Will fail silently if the setUrl method has not been called.
42033      *   This does not activate the panel, just updates its content.
42034      */
42035     refresh : function(){
42036         if(this.refreshDelegate){
42037            this.loaded = false;
42038            this.refreshDelegate();
42039         }
42040     },
42041
42042     /** @private */
42043     _setLoaded : function(){
42044         this.loaded = true;
42045     },
42046
42047     /** @private */
42048     closeClick : function(e){
42049         var o = {};
42050         e.stopEvent();
42051         this.fireEvent("beforeclose", this, o);
42052         if(o.cancel !== true){
42053             this.tabPanel.removeTab(this.id);
42054         }
42055     },
42056     /**
42057      * The text displayed in the tooltip for the close icon.
42058      * @type String
42059      */
42060     closeText : "Close this tab"
42061 });
42062 /**
42063 *    This script refer to:
42064 *    Title: International Telephone Input
42065 *    Author: Jack O'Connor
42066 *    Code version:  v12.1.12
42067 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42068 **/
42069
42070 Roo.bootstrap.form.PhoneInputData = function() {
42071     var d = [
42072       [
42073         "Afghanistan (‫افغانستان‬‎)",
42074         "af",
42075         "93"
42076       ],
42077       [
42078         "Albania (Shqipëri)",
42079         "al",
42080         "355"
42081       ],
42082       [
42083         "Algeria (‫الجزائر‬‎)",
42084         "dz",
42085         "213"
42086       ],
42087       [
42088         "American Samoa",
42089         "as",
42090         "1684"
42091       ],
42092       [
42093         "Andorra",
42094         "ad",
42095         "376"
42096       ],
42097       [
42098         "Angola",
42099         "ao",
42100         "244"
42101       ],
42102       [
42103         "Anguilla",
42104         "ai",
42105         "1264"
42106       ],
42107       [
42108         "Antigua and Barbuda",
42109         "ag",
42110         "1268"
42111       ],
42112       [
42113         "Argentina",
42114         "ar",
42115         "54"
42116       ],
42117       [
42118         "Armenia (Հայաստան)",
42119         "am",
42120         "374"
42121       ],
42122       [
42123         "Aruba",
42124         "aw",
42125         "297"
42126       ],
42127       [
42128         "Australia",
42129         "au",
42130         "61",
42131         0
42132       ],
42133       [
42134         "Austria (Österreich)",
42135         "at",
42136         "43"
42137       ],
42138       [
42139         "Azerbaijan (Azərbaycan)",
42140         "az",
42141         "994"
42142       ],
42143       [
42144         "Bahamas",
42145         "bs",
42146         "1242"
42147       ],
42148       [
42149         "Bahrain (‫البحرين‬‎)",
42150         "bh",
42151         "973"
42152       ],
42153       [
42154         "Bangladesh (বাংলাদেশ)",
42155         "bd",
42156         "880"
42157       ],
42158       [
42159         "Barbados",
42160         "bb",
42161         "1246"
42162       ],
42163       [
42164         "Belarus (Беларусь)",
42165         "by",
42166         "375"
42167       ],
42168       [
42169         "Belgium (België)",
42170         "be",
42171         "32"
42172       ],
42173       [
42174         "Belize",
42175         "bz",
42176         "501"
42177       ],
42178       [
42179         "Benin (Bénin)",
42180         "bj",
42181         "229"
42182       ],
42183       [
42184         "Bermuda",
42185         "bm",
42186         "1441"
42187       ],
42188       [
42189         "Bhutan (འབྲུག)",
42190         "bt",
42191         "975"
42192       ],
42193       [
42194         "Bolivia",
42195         "bo",
42196         "591"
42197       ],
42198       [
42199         "Bosnia and Herzegovina (Босна и Херцеговина)",
42200         "ba",
42201         "387"
42202       ],
42203       [
42204         "Botswana",
42205         "bw",
42206         "267"
42207       ],
42208       [
42209         "Brazil (Brasil)",
42210         "br",
42211         "55"
42212       ],
42213       [
42214         "British Indian Ocean Territory",
42215         "io",
42216         "246"
42217       ],
42218       [
42219         "British Virgin Islands",
42220         "vg",
42221         "1284"
42222       ],
42223       [
42224         "Brunei",
42225         "bn",
42226         "673"
42227       ],
42228       [
42229         "Bulgaria (България)",
42230         "bg",
42231         "359"
42232       ],
42233       [
42234         "Burkina Faso",
42235         "bf",
42236         "226"
42237       ],
42238       [
42239         "Burundi (Uburundi)",
42240         "bi",
42241         "257"
42242       ],
42243       [
42244         "Cambodia (កម្ពុជា)",
42245         "kh",
42246         "855"
42247       ],
42248       [
42249         "Cameroon (Cameroun)",
42250         "cm",
42251         "237"
42252       ],
42253       [
42254         "Canada",
42255         "ca",
42256         "1",
42257         1,
42258         ["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"]
42259       ],
42260       [
42261         "Cape Verde (Kabu Verdi)",
42262         "cv",
42263         "238"
42264       ],
42265       [
42266         "Caribbean Netherlands",
42267         "bq",
42268         "599",
42269         1
42270       ],
42271       [
42272         "Cayman Islands",
42273         "ky",
42274         "1345"
42275       ],
42276       [
42277         "Central African Republic (République centrafricaine)",
42278         "cf",
42279         "236"
42280       ],
42281       [
42282         "Chad (Tchad)",
42283         "td",
42284         "235"
42285       ],
42286       [
42287         "Chile",
42288         "cl",
42289         "56"
42290       ],
42291       [
42292         "China (中国)",
42293         "cn",
42294         "86"
42295       ],
42296       [
42297         "Christmas Island",
42298         "cx",
42299         "61",
42300         2
42301       ],
42302       [
42303         "Cocos (Keeling) Islands",
42304         "cc",
42305         "61",
42306         1
42307       ],
42308       [
42309         "Colombia",
42310         "co",
42311         "57"
42312       ],
42313       [
42314         "Comoros (‫جزر القمر‬‎)",
42315         "km",
42316         "269"
42317       ],
42318       [
42319         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42320         "cd",
42321         "243"
42322       ],
42323       [
42324         "Congo (Republic) (Congo-Brazzaville)",
42325         "cg",
42326         "242"
42327       ],
42328       [
42329         "Cook Islands",
42330         "ck",
42331         "682"
42332       ],
42333       [
42334         "Costa Rica",
42335         "cr",
42336         "506"
42337       ],
42338       [
42339         "Côte d’Ivoire",
42340         "ci",
42341         "225"
42342       ],
42343       [
42344         "Croatia (Hrvatska)",
42345         "hr",
42346         "385"
42347       ],
42348       [
42349         "Cuba",
42350         "cu",
42351         "53"
42352       ],
42353       [
42354         "Curaçao",
42355         "cw",
42356         "599",
42357         0
42358       ],
42359       [
42360         "Cyprus (Κύπρος)",
42361         "cy",
42362         "357"
42363       ],
42364       [
42365         "Czech Republic (Česká republika)",
42366         "cz",
42367         "420"
42368       ],
42369       [
42370         "Denmark (Danmark)",
42371         "dk",
42372         "45"
42373       ],
42374       [
42375         "Djibouti",
42376         "dj",
42377         "253"
42378       ],
42379       [
42380         "Dominica",
42381         "dm",
42382         "1767"
42383       ],
42384       [
42385         "Dominican Republic (República Dominicana)",
42386         "do",
42387         "1",
42388         2,
42389         ["809", "829", "849"]
42390       ],
42391       [
42392         "Ecuador",
42393         "ec",
42394         "593"
42395       ],
42396       [
42397         "Egypt (‫مصر‬‎)",
42398         "eg",
42399         "20"
42400       ],
42401       [
42402         "El Salvador",
42403         "sv",
42404         "503"
42405       ],
42406       [
42407         "Equatorial Guinea (Guinea Ecuatorial)",
42408         "gq",
42409         "240"
42410       ],
42411       [
42412         "Eritrea",
42413         "er",
42414         "291"
42415       ],
42416       [
42417         "Estonia (Eesti)",
42418         "ee",
42419         "372"
42420       ],
42421       [
42422         "Ethiopia",
42423         "et",
42424         "251"
42425       ],
42426       [
42427         "Falkland Islands (Islas Malvinas)",
42428         "fk",
42429         "500"
42430       ],
42431       [
42432         "Faroe Islands (Føroyar)",
42433         "fo",
42434         "298"
42435       ],
42436       [
42437         "Fiji",
42438         "fj",
42439         "679"
42440       ],
42441       [
42442         "Finland (Suomi)",
42443         "fi",
42444         "358",
42445         0
42446       ],
42447       [
42448         "France",
42449         "fr",
42450         "33"
42451       ],
42452       [
42453         "French Guiana (Guyane française)",
42454         "gf",
42455         "594"
42456       ],
42457       [
42458         "French Polynesia (Polynésie française)",
42459         "pf",
42460         "689"
42461       ],
42462       [
42463         "Gabon",
42464         "ga",
42465         "241"
42466       ],
42467       [
42468         "Gambia",
42469         "gm",
42470         "220"
42471       ],
42472       [
42473         "Georgia (საქართველო)",
42474         "ge",
42475         "995"
42476       ],
42477       [
42478         "Germany (Deutschland)",
42479         "de",
42480         "49"
42481       ],
42482       [
42483         "Ghana (Gaana)",
42484         "gh",
42485         "233"
42486       ],
42487       [
42488         "Gibraltar",
42489         "gi",
42490         "350"
42491       ],
42492       [
42493         "Greece (Ελλάδα)",
42494         "gr",
42495         "30"
42496       ],
42497       [
42498         "Greenland (Kalaallit Nunaat)",
42499         "gl",
42500         "299"
42501       ],
42502       [
42503         "Grenada",
42504         "gd",
42505         "1473"
42506       ],
42507       [
42508         "Guadeloupe",
42509         "gp",
42510         "590",
42511         0
42512       ],
42513       [
42514         "Guam",
42515         "gu",
42516         "1671"
42517       ],
42518       [
42519         "Guatemala",
42520         "gt",
42521         "502"
42522       ],
42523       [
42524         "Guernsey",
42525         "gg",
42526         "44",
42527         1
42528       ],
42529       [
42530         "Guinea (Guinée)",
42531         "gn",
42532         "224"
42533       ],
42534       [
42535         "Guinea-Bissau (Guiné Bissau)",
42536         "gw",
42537         "245"
42538       ],
42539       [
42540         "Guyana",
42541         "gy",
42542         "592"
42543       ],
42544       [
42545         "Haiti",
42546         "ht",
42547         "509"
42548       ],
42549       [
42550         "Honduras",
42551         "hn",
42552         "504"
42553       ],
42554       [
42555         "Hong Kong (香港)",
42556         "hk",
42557         "852"
42558       ],
42559       [
42560         "Hungary (Magyarország)",
42561         "hu",
42562         "36"
42563       ],
42564       [
42565         "Iceland (Ísland)",
42566         "is",
42567         "354"
42568       ],
42569       [
42570         "India (भारत)",
42571         "in",
42572         "91"
42573       ],
42574       [
42575         "Indonesia",
42576         "id",
42577         "62"
42578       ],
42579       [
42580         "Iran (‫ایران‬‎)",
42581         "ir",
42582         "98"
42583       ],
42584       [
42585         "Iraq (‫العراق‬‎)",
42586         "iq",
42587         "964"
42588       ],
42589       [
42590         "Ireland",
42591         "ie",
42592         "353"
42593       ],
42594       [
42595         "Isle of Man",
42596         "im",
42597         "44",
42598         2
42599       ],
42600       [
42601         "Israel (‫ישראל‬‎)",
42602         "il",
42603         "972"
42604       ],
42605       [
42606         "Italy (Italia)",
42607         "it",
42608         "39",
42609         0
42610       ],
42611       [
42612         "Jamaica",
42613         "jm",
42614         "1876"
42615       ],
42616       [
42617         "Japan (日本)",
42618         "jp",
42619         "81"
42620       ],
42621       [
42622         "Jersey",
42623         "je",
42624         "44",
42625         3
42626       ],
42627       [
42628         "Jordan (‫الأردن‬‎)",
42629         "jo",
42630         "962"
42631       ],
42632       [
42633         "Kazakhstan (Казахстан)",
42634         "kz",
42635         "7",
42636         1
42637       ],
42638       [
42639         "Kenya",
42640         "ke",
42641         "254"
42642       ],
42643       [
42644         "Kiribati",
42645         "ki",
42646         "686"
42647       ],
42648       [
42649         "Kosovo",
42650         "xk",
42651         "383"
42652       ],
42653       [
42654         "Kuwait (‫الكويت‬‎)",
42655         "kw",
42656         "965"
42657       ],
42658       [
42659         "Kyrgyzstan (Кыргызстан)",
42660         "kg",
42661         "996"
42662       ],
42663       [
42664         "Laos (ລາວ)",
42665         "la",
42666         "856"
42667       ],
42668       [
42669         "Latvia (Latvija)",
42670         "lv",
42671         "371"
42672       ],
42673       [
42674         "Lebanon (‫لبنان‬‎)",
42675         "lb",
42676         "961"
42677       ],
42678       [
42679         "Lesotho",
42680         "ls",
42681         "266"
42682       ],
42683       [
42684         "Liberia",
42685         "lr",
42686         "231"
42687       ],
42688       [
42689         "Libya (‫ليبيا‬‎)",
42690         "ly",
42691         "218"
42692       ],
42693       [
42694         "Liechtenstein",
42695         "li",
42696         "423"
42697       ],
42698       [
42699         "Lithuania (Lietuva)",
42700         "lt",
42701         "370"
42702       ],
42703       [
42704         "Luxembourg",
42705         "lu",
42706         "352"
42707       ],
42708       [
42709         "Macau (澳門)",
42710         "mo",
42711         "853"
42712       ],
42713       [
42714         "Macedonia (FYROM) (Македонија)",
42715         "mk",
42716         "389"
42717       ],
42718       [
42719         "Madagascar (Madagasikara)",
42720         "mg",
42721         "261"
42722       ],
42723       [
42724         "Malawi",
42725         "mw",
42726         "265"
42727       ],
42728       [
42729         "Malaysia",
42730         "my",
42731         "60"
42732       ],
42733       [
42734         "Maldives",
42735         "mv",
42736         "960"
42737       ],
42738       [
42739         "Mali",
42740         "ml",
42741         "223"
42742       ],
42743       [
42744         "Malta",
42745         "mt",
42746         "356"
42747       ],
42748       [
42749         "Marshall Islands",
42750         "mh",
42751         "692"
42752       ],
42753       [
42754         "Martinique",
42755         "mq",
42756         "596"
42757       ],
42758       [
42759         "Mauritania (‫موريتانيا‬‎)",
42760         "mr",
42761         "222"
42762       ],
42763       [
42764         "Mauritius (Moris)",
42765         "mu",
42766         "230"
42767       ],
42768       [
42769         "Mayotte",
42770         "yt",
42771         "262",
42772         1
42773       ],
42774       [
42775         "Mexico (México)",
42776         "mx",
42777         "52"
42778       ],
42779       [
42780         "Micronesia",
42781         "fm",
42782         "691"
42783       ],
42784       [
42785         "Moldova (Republica Moldova)",
42786         "md",
42787         "373"
42788       ],
42789       [
42790         "Monaco",
42791         "mc",
42792         "377"
42793       ],
42794       [
42795         "Mongolia (Монгол)",
42796         "mn",
42797         "976"
42798       ],
42799       [
42800         "Montenegro (Crna Gora)",
42801         "me",
42802         "382"
42803       ],
42804       [
42805         "Montserrat",
42806         "ms",
42807         "1664"
42808       ],
42809       [
42810         "Morocco (‫المغرب‬‎)",
42811         "ma",
42812         "212",
42813         0
42814       ],
42815       [
42816         "Mozambique (Moçambique)",
42817         "mz",
42818         "258"
42819       ],
42820       [
42821         "Myanmar (Burma) (မြန်မာ)",
42822         "mm",
42823         "95"
42824       ],
42825       [
42826         "Namibia (Namibië)",
42827         "na",
42828         "264"
42829       ],
42830       [
42831         "Nauru",
42832         "nr",
42833         "674"
42834       ],
42835       [
42836         "Nepal (नेपाल)",
42837         "np",
42838         "977"
42839       ],
42840       [
42841         "Netherlands (Nederland)",
42842         "nl",
42843         "31"
42844       ],
42845       [
42846         "New Caledonia (Nouvelle-Calédonie)",
42847         "nc",
42848         "687"
42849       ],
42850       [
42851         "New Zealand",
42852         "nz",
42853         "64"
42854       ],
42855       [
42856         "Nicaragua",
42857         "ni",
42858         "505"
42859       ],
42860       [
42861         "Niger (Nijar)",
42862         "ne",
42863         "227"
42864       ],
42865       [
42866         "Nigeria",
42867         "ng",
42868         "234"
42869       ],
42870       [
42871         "Niue",
42872         "nu",
42873         "683"
42874       ],
42875       [
42876         "Norfolk Island",
42877         "nf",
42878         "672"
42879       ],
42880       [
42881         "North Korea (조선 민주주의 인민 공화국)",
42882         "kp",
42883         "850"
42884       ],
42885       [
42886         "Northern Mariana Islands",
42887         "mp",
42888         "1670"
42889       ],
42890       [
42891         "Norway (Norge)",
42892         "no",
42893         "47",
42894         0
42895       ],
42896       [
42897         "Oman (‫عُمان‬‎)",
42898         "om",
42899         "968"
42900       ],
42901       [
42902         "Pakistan (‫پاکستان‬‎)",
42903         "pk",
42904         "92"
42905       ],
42906       [
42907         "Palau",
42908         "pw",
42909         "680"
42910       ],
42911       [
42912         "Palestine (‫فلسطين‬‎)",
42913         "ps",
42914         "970"
42915       ],
42916       [
42917         "Panama (Panamá)",
42918         "pa",
42919         "507"
42920       ],
42921       [
42922         "Papua New Guinea",
42923         "pg",
42924         "675"
42925       ],
42926       [
42927         "Paraguay",
42928         "py",
42929         "595"
42930       ],
42931       [
42932         "Peru (Perú)",
42933         "pe",
42934         "51"
42935       ],
42936       [
42937         "Philippines",
42938         "ph",
42939         "63"
42940       ],
42941       [
42942         "Poland (Polska)",
42943         "pl",
42944         "48"
42945       ],
42946       [
42947         "Portugal",
42948         "pt",
42949         "351"
42950       ],
42951       [
42952         "Puerto Rico",
42953         "pr",
42954         "1",
42955         3,
42956         ["787", "939"]
42957       ],
42958       [
42959         "Qatar (‫قطر‬‎)",
42960         "qa",
42961         "974"
42962       ],
42963       [
42964         "Réunion (La Réunion)",
42965         "re",
42966         "262",
42967         0
42968       ],
42969       [
42970         "Romania (România)",
42971         "ro",
42972         "40"
42973       ],
42974       [
42975         "Russia (Россия)",
42976         "ru",
42977         "7",
42978         0
42979       ],
42980       [
42981         "Rwanda",
42982         "rw",
42983         "250"
42984       ],
42985       [
42986         "Saint Barthélemy",
42987         "bl",
42988         "590",
42989         1
42990       ],
42991       [
42992         "Saint Helena",
42993         "sh",
42994         "290"
42995       ],
42996       [
42997         "Saint Kitts and Nevis",
42998         "kn",
42999         "1869"
43000       ],
43001       [
43002         "Saint Lucia",
43003         "lc",
43004         "1758"
43005       ],
43006       [
43007         "Saint Martin (Saint-Martin (partie française))",
43008         "mf",
43009         "590",
43010         2
43011       ],
43012       [
43013         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43014         "pm",
43015         "508"
43016       ],
43017       [
43018         "Saint Vincent and the Grenadines",
43019         "vc",
43020         "1784"
43021       ],
43022       [
43023         "Samoa",
43024         "ws",
43025         "685"
43026       ],
43027       [
43028         "San Marino",
43029         "sm",
43030         "378"
43031       ],
43032       [
43033         "São Tomé and Príncipe (São Tomé e Príncipe)",
43034         "st",
43035         "239"
43036       ],
43037       [
43038         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43039         "sa",
43040         "966"
43041       ],
43042       [
43043         "Senegal (Sénégal)",
43044         "sn",
43045         "221"
43046       ],
43047       [
43048         "Serbia (Србија)",
43049         "rs",
43050         "381"
43051       ],
43052       [
43053         "Seychelles",
43054         "sc",
43055         "248"
43056       ],
43057       [
43058         "Sierra Leone",
43059         "sl",
43060         "232"
43061       ],
43062       [
43063         "Singapore",
43064         "sg",
43065         "65"
43066       ],
43067       [
43068         "Sint Maarten",
43069         "sx",
43070         "1721"
43071       ],
43072       [
43073         "Slovakia (Slovensko)",
43074         "sk",
43075         "421"
43076       ],
43077       [
43078         "Slovenia (Slovenija)",
43079         "si",
43080         "386"
43081       ],
43082       [
43083         "Solomon Islands",
43084         "sb",
43085         "677"
43086       ],
43087       [
43088         "Somalia (Soomaaliya)",
43089         "so",
43090         "252"
43091       ],
43092       [
43093         "South Africa",
43094         "za",
43095         "27"
43096       ],
43097       [
43098         "South Korea (대한민국)",
43099         "kr",
43100         "82"
43101       ],
43102       [
43103         "South Sudan (‫جنوب السودان‬‎)",
43104         "ss",
43105         "211"
43106       ],
43107       [
43108         "Spain (España)",
43109         "es",
43110         "34"
43111       ],
43112       [
43113         "Sri Lanka (ශ්‍රී ලංකාව)",
43114         "lk",
43115         "94"
43116       ],
43117       [
43118         "Sudan (‫السودان‬‎)",
43119         "sd",
43120         "249"
43121       ],
43122       [
43123         "Suriname",
43124         "sr",
43125         "597"
43126       ],
43127       [
43128         "Svalbard and Jan Mayen",
43129         "sj",
43130         "47",
43131         1
43132       ],
43133       [
43134         "Swaziland",
43135         "sz",
43136         "268"
43137       ],
43138       [
43139         "Sweden (Sverige)",
43140         "se",
43141         "46"
43142       ],
43143       [
43144         "Switzerland (Schweiz)",
43145         "ch",
43146         "41"
43147       ],
43148       [
43149         "Syria (‫سوريا‬‎)",
43150         "sy",
43151         "963"
43152       ],
43153       [
43154         "Taiwan (台灣)",
43155         "tw",
43156         "886"
43157       ],
43158       [
43159         "Tajikistan",
43160         "tj",
43161         "992"
43162       ],
43163       [
43164         "Tanzania",
43165         "tz",
43166         "255"
43167       ],
43168       [
43169         "Thailand (ไทย)",
43170         "th",
43171         "66"
43172       ],
43173       [
43174         "Timor-Leste",
43175         "tl",
43176         "670"
43177       ],
43178       [
43179         "Togo",
43180         "tg",
43181         "228"
43182       ],
43183       [
43184         "Tokelau",
43185         "tk",
43186         "690"
43187       ],
43188       [
43189         "Tonga",
43190         "to",
43191         "676"
43192       ],
43193       [
43194         "Trinidad and Tobago",
43195         "tt",
43196         "1868"
43197       ],
43198       [
43199         "Tunisia (‫تونس‬‎)",
43200         "tn",
43201         "216"
43202       ],
43203       [
43204         "Turkey (Türkiye)",
43205         "tr",
43206         "90"
43207       ],
43208       [
43209         "Turkmenistan",
43210         "tm",
43211         "993"
43212       ],
43213       [
43214         "Turks and Caicos Islands",
43215         "tc",
43216         "1649"
43217       ],
43218       [
43219         "Tuvalu",
43220         "tv",
43221         "688"
43222       ],
43223       [
43224         "U.S. Virgin Islands",
43225         "vi",
43226         "1340"
43227       ],
43228       [
43229         "Uganda",
43230         "ug",
43231         "256"
43232       ],
43233       [
43234         "Ukraine (Україна)",
43235         "ua",
43236         "380"
43237       ],
43238       [
43239         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43240         "ae",
43241         "971"
43242       ],
43243       [
43244         "United Kingdom",
43245         "gb",
43246         "44",
43247         0
43248       ],
43249       [
43250         "United States",
43251         "us",
43252         "1",
43253         0
43254       ],
43255       [
43256         "Uruguay",
43257         "uy",
43258         "598"
43259       ],
43260       [
43261         "Uzbekistan (Oʻzbekiston)",
43262         "uz",
43263         "998"
43264       ],
43265       [
43266         "Vanuatu",
43267         "vu",
43268         "678"
43269       ],
43270       [
43271         "Vatican City (Città del Vaticano)",
43272         "va",
43273         "39",
43274         1
43275       ],
43276       [
43277         "Venezuela",
43278         "ve",
43279         "58"
43280       ],
43281       [
43282         "Vietnam (Việt Nam)",
43283         "vn",
43284         "84"
43285       ],
43286       [
43287         "Wallis and Futuna (Wallis-et-Futuna)",
43288         "wf",
43289         "681"
43290       ],
43291       [
43292         "Western Sahara (‫الصحراء الغربية‬‎)",
43293         "eh",
43294         "212",
43295         1
43296       ],
43297       [
43298         "Yemen (‫اليمن‬‎)",
43299         "ye",
43300         "967"
43301       ],
43302       [
43303         "Zambia",
43304         "zm",
43305         "260"
43306       ],
43307       [
43308         "Zimbabwe",
43309         "zw",
43310         "263"
43311       ],
43312       [
43313         "Åland Islands",
43314         "ax",
43315         "358",
43316         1
43317       ]
43318   ];
43319   
43320   return d;
43321 }/**
43322 *    This script refer to:
43323 *    Title: International Telephone Input
43324 *    Author: Jack O'Connor
43325 *    Code version:  v12.1.12
43326 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43327 **/
43328
43329 /**
43330  * @class Roo.bootstrap.form.PhoneInput
43331  * @extends Roo.bootstrap.form.TriggerField
43332  * An input with International dial-code selection
43333  
43334  * @cfg {String} defaultDialCode default '+852'
43335  * @cfg {Array} preferedCountries default []
43336   
43337  * @constructor
43338  * Create a new PhoneInput.
43339  * @param {Object} config Configuration options
43340  */
43341
43342 Roo.bootstrap.form.PhoneInput = function(config) {
43343     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43344 };
43345
43346 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43347         /**
43348         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43349         */
43350         listWidth: undefined,
43351         
43352         selectedClass: 'active',
43353         
43354         invalidClass : "has-warning",
43355         
43356         validClass: 'has-success',
43357         
43358         allowed: '0123456789',
43359         
43360         max_length: 15,
43361         
43362         /**
43363          * @cfg {String} defaultDialCode The default dial code when initializing the input
43364          */
43365         defaultDialCode: '+852',
43366         
43367         /**
43368          * @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
43369          */
43370         preferedCountries: false,
43371         
43372         getAutoCreate : function()
43373         {
43374             var data = Roo.bootstrap.form.PhoneInputData();
43375             var align = this.labelAlign || this.parentLabelAlign();
43376             var id = Roo.id();
43377             
43378             this.allCountries = [];
43379             this.dialCodeMapping = [];
43380             
43381             for (var i = 0; i < data.length; i++) {
43382               var c = data[i];
43383               this.allCountries[i] = {
43384                 name: c[0],
43385                 iso2: c[1],
43386                 dialCode: c[2],
43387                 priority: c[3] || 0,
43388                 areaCodes: c[4] || null
43389               };
43390               this.dialCodeMapping[c[2]] = {
43391                   name: c[0],
43392                   iso2: c[1],
43393                   priority: c[3] || 0,
43394                   areaCodes: c[4] || null
43395               };
43396             }
43397             
43398             var cfg = {
43399                 cls: 'form-group',
43400                 cn: []
43401             };
43402             
43403             var input =  {
43404                 tag: 'input',
43405                 id : id,
43406                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43407                 maxlength: this.max_length,
43408                 cls : 'form-control tel-input',
43409                 autocomplete: 'new-password'
43410             };
43411             
43412             var hiddenInput = {
43413                 tag: 'input',
43414                 type: 'hidden',
43415                 cls: 'hidden-tel-input'
43416             };
43417             
43418             if (this.name) {
43419                 hiddenInput.name = this.name;
43420             }
43421             
43422             if (this.disabled) {
43423                 input.disabled = true;
43424             }
43425             
43426             var flag_container = {
43427                 tag: 'div',
43428                 cls: 'flag-box',
43429                 cn: [
43430                     {
43431                         tag: 'div',
43432                         cls: 'flag'
43433                     },
43434                     {
43435                         tag: 'div',
43436                         cls: 'caret'
43437                     }
43438                 ]
43439             };
43440             
43441             var box = {
43442                 tag: 'div',
43443                 cls: this.hasFeedback ? 'has-feedback' : '',
43444                 cn: [
43445                     hiddenInput,
43446                     input,
43447                     {
43448                         tag: 'input',
43449                         cls: 'dial-code-holder',
43450                         disabled: true
43451                     }
43452                 ]
43453             };
43454             
43455             var container = {
43456                 cls: 'roo-select2-container input-group',
43457                 cn: [
43458                     flag_container,
43459                     box
43460                 ]
43461             };
43462             
43463             if (this.fieldLabel.length) {
43464                 var indicator = {
43465                     tag: 'i',
43466                     tooltip: 'This field is required'
43467                 };
43468                 
43469                 var label = {
43470                     tag: 'label',
43471                     'for':  id,
43472                     cls: 'control-label',
43473                     cn: []
43474                 };
43475                 
43476                 var label_text = {
43477                     tag: 'span',
43478                     html: this.fieldLabel
43479                 };
43480                 
43481                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43482                 label.cn = [
43483                     indicator,
43484                     label_text
43485                 ];
43486                 
43487                 if(this.indicatorpos == 'right') {
43488                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43489                     label.cn = [
43490                         label_text,
43491                         indicator
43492                     ];
43493                 }
43494                 
43495                 if(align == 'left') {
43496                     container = {
43497                         tag: 'div',
43498                         cn: [
43499                             container
43500                         ]
43501                     };
43502                     
43503                     if(this.labelWidth > 12){
43504                         label.style = "width: " + this.labelWidth + 'px';
43505                     }
43506                     if(this.labelWidth < 13 && this.labelmd == 0){
43507                         this.labelmd = this.labelWidth;
43508                     }
43509                     if(this.labellg > 0){
43510                         label.cls += ' col-lg-' + this.labellg;
43511                         input.cls += ' col-lg-' + (12 - this.labellg);
43512                     }
43513                     if(this.labelmd > 0){
43514                         label.cls += ' col-md-' + this.labelmd;
43515                         container.cls += ' col-md-' + (12 - this.labelmd);
43516                     }
43517                     if(this.labelsm > 0){
43518                         label.cls += ' col-sm-' + this.labelsm;
43519                         container.cls += ' col-sm-' + (12 - this.labelsm);
43520                     }
43521                     if(this.labelxs > 0){
43522                         label.cls += ' col-xs-' + this.labelxs;
43523                         container.cls += ' col-xs-' + (12 - this.labelxs);
43524                     }
43525                 }
43526             }
43527             
43528             cfg.cn = [
43529                 label,
43530                 container
43531             ];
43532             
43533             var settings = this;
43534             
43535             ['xs','sm','md','lg'].map(function(size){
43536                 if (settings[size]) {
43537                     cfg.cls += ' col-' + size + '-' + settings[size];
43538                 }
43539             });
43540             
43541             this.store = new Roo.data.Store({
43542                 proxy : new Roo.data.MemoryProxy({}),
43543                 reader : new Roo.data.JsonReader({
43544                     fields : [
43545                         {
43546                             'name' : 'name',
43547                             'type' : 'string'
43548                         },
43549                         {
43550                             'name' : 'iso2',
43551                             'type' : 'string'
43552                         },
43553                         {
43554                             'name' : 'dialCode',
43555                             'type' : 'string'
43556                         },
43557                         {
43558                             'name' : 'priority',
43559                             'type' : 'string'
43560                         },
43561                         {
43562                             'name' : 'areaCodes',
43563                             'type' : 'string'
43564                         }
43565                     ]
43566                 })
43567             });
43568             
43569             if(!this.preferedCountries) {
43570                 this.preferedCountries = [
43571                     'hk',
43572                     'gb',
43573                     'us'
43574                 ];
43575             }
43576             
43577             var p = this.preferedCountries.reverse();
43578             
43579             if(p) {
43580                 for (var i = 0; i < p.length; i++) {
43581                     for (var j = 0; j < this.allCountries.length; j++) {
43582                         if(this.allCountries[j].iso2 == p[i]) {
43583                             var t = this.allCountries[j];
43584                             this.allCountries.splice(j,1);
43585                             this.allCountries.unshift(t);
43586                         }
43587                     } 
43588                 }
43589             }
43590             
43591             this.store.proxy.data = {
43592                 success: true,
43593                 data: this.allCountries
43594             };
43595             
43596             return cfg;
43597         },
43598         
43599         initEvents : function()
43600         {
43601             this.createList();
43602             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43603             
43604             this.indicator = this.indicatorEl();
43605             this.flag = this.flagEl();
43606             this.dialCodeHolder = this.dialCodeHolderEl();
43607             
43608             this.trigger = this.el.select('div.flag-box',true).first();
43609             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43610             
43611             var _this = this;
43612             
43613             (function(){
43614                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43615                 _this.list.setWidth(lw);
43616             }).defer(100);
43617             
43618             this.list.on('mouseover', this.onViewOver, this);
43619             this.list.on('mousemove', this.onViewMove, this);
43620             this.inputEl().on("keyup", this.onKeyUp, this);
43621             this.inputEl().on("keypress", this.onKeyPress, this);
43622             
43623             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43624
43625             this.view = new Roo.View(this.list, this.tpl, {
43626                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43627             });
43628             
43629             this.view.on('click', this.onViewClick, this);
43630             this.setValue(this.defaultDialCode);
43631         },
43632         
43633         onTriggerClick : function(e)
43634         {
43635             Roo.log('trigger click');
43636             if(this.disabled){
43637                 return;
43638             }
43639             
43640             if(this.isExpanded()){
43641                 this.collapse();
43642                 this.hasFocus = false;
43643             }else {
43644                 this.store.load({});
43645                 this.hasFocus = true;
43646                 this.expand();
43647             }
43648         },
43649         
43650         isExpanded : function()
43651         {
43652             return this.list.isVisible();
43653         },
43654         
43655         collapse : function()
43656         {
43657             if(!this.isExpanded()){
43658                 return;
43659             }
43660             this.list.hide();
43661             Roo.get(document).un('mousedown', this.collapseIf, this);
43662             Roo.get(document).un('mousewheel', this.collapseIf, this);
43663             this.fireEvent('collapse', this);
43664             this.validate();
43665         },
43666         
43667         expand : function()
43668         {
43669             Roo.log('expand');
43670
43671             if(this.isExpanded() || !this.hasFocus){
43672                 return;
43673             }
43674             
43675             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43676             this.list.setWidth(lw);
43677             
43678             this.list.show();
43679             this.restrictHeight();
43680             
43681             Roo.get(document).on('mousedown', this.collapseIf, this);
43682             Roo.get(document).on('mousewheel', this.collapseIf, this);
43683             
43684             this.fireEvent('expand', this);
43685         },
43686         
43687         restrictHeight : function()
43688         {
43689             this.list.alignTo(this.inputEl(), this.listAlign);
43690             this.list.alignTo(this.inputEl(), this.listAlign);
43691         },
43692         
43693         onViewOver : function(e, t)
43694         {
43695             if(this.inKeyMode){
43696                 return;
43697             }
43698             var item = this.view.findItemFromChild(t);
43699             
43700             if(item){
43701                 var index = this.view.indexOf(item);
43702                 this.select(index, false);
43703             }
43704         },
43705
43706         // private
43707         onViewClick : function(view, doFocus, el, e)
43708         {
43709             var index = this.view.getSelectedIndexes()[0];
43710             
43711             var r = this.store.getAt(index);
43712             
43713             if(r){
43714                 this.onSelect(r, index);
43715             }
43716             if(doFocus !== false && !this.blockFocus){
43717                 this.inputEl().focus();
43718             }
43719         },
43720         
43721         onViewMove : function(e, t)
43722         {
43723             this.inKeyMode = false;
43724         },
43725         
43726         select : function(index, scrollIntoView)
43727         {
43728             this.selectedIndex = index;
43729             this.view.select(index);
43730             if(scrollIntoView !== false){
43731                 var el = this.view.getNode(index);
43732                 if(el){
43733                     this.list.scrollChildIntoView(el, false);
43734                 }
43735             }
43736         },
43737         
43738         createList : function()
43739         {
43740             this.list = Roo.get(document.body).createChild({
43741                 tag: 'ul',
43742                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43743                 style: 'display:none'
43744             });
43745             
43746             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43747         },
43748         
43749         collapseIf : function(e)
43750         {
43751             var in_combo  = e.within(this.el);
43752             var in_list =  e.within(this.list);
43753             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43754             
43755             if (in_combo || in_list || is_list) {
43756                 return;
43757             }
43758             this.collapse();
43759         },
43760         
43761         onSelect : function(record, index)
43762         {
43763             if(this.fireEvent('beforeselect', this, record, index) !== false){
43764                 
43765                 this.setFlagClass(record.data.iso2);
43766                 this.setDialCode(record.data.dialCode);
43767                 this.hasFocus = false;
43768                 this.collapse();
43769                 this.fireEvent('select', this, record, index);
43770             }
43771         },
43772         
43773         flagEl : function()
43774         {
43775             var flag = this.el.select('div.flag',true).first();
43776             if(!flag){
43777                 return false;
43778             }
43779             return flag;
43780         },
43781         
43782         dialCodeHolderEl : function()
43783         {
43784             var d = this.el.select('input.dial-code-holder',true).first();
43785             if(!d){
43786                 return false;
43787             }
43788             return d;
43789         },
43790         
43791         setDialCode : function(v)
43792         {
43793             this.dialCodeHolder.dom.value = '+'+v;
43794         },
43795         
43796         setFlagClass : function(n)
43797         {
43798             this.flag.dom.className = 'flag '+n;
43799         },
43800         
43801         getValue : function()
43802         {
43803             var v = this.inputEl().getValue();
43804             if(this.dialCodeHolder) {
43805                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43806             }
43807             return v;
43808         },
43809         
43810         setValue : function(v)
43811         {
43812             var d = this.getDialCode(v);
43813             
43814             //invalid dial code
43815             if(v.length == 0 || !d || d.length == 0) {
43816                 if(this.rendered){
43817                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43818                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43819                 }
43820                 return;
43821             }
43822             
43823             //valid dial code
43824             this.setFlagClass(this.dialCodeMapping[d].iso2);
43825             this.setDialCode(d);
43826             this.inputEl().dom.value = v.replace('+'+d,'');
43827             this.hiddenEl().dom.value = this.getValue();
43828             
43829             this.validate();
43830         },
43831         
43832         getDialCode : function(v)
43833         {
43834             v = v ||  '';
43835             
43836             if (v.length == 0) {
43837                 return this.dialCodeHolder.dom.value;
43838             }
43839             
43840             var dialCode = "";
43841             if (v.charAt(0) != "+") {
43842                 return false;
43843             }
43844             var numericChars = "";
43845             for (var i = 1; i < v.length; i++) {
43846               var c = v.charAt(i);
43847               if (!isNaN(c)) {
43848                 numericChars += c;
43849                 if (this.dialCodeMapping[numericChars]) {
43850                   dialCode = v.substr(1, i);
43851                 }
43852                 if (numericChars.length == 4) {
43853                   break;
43854                 }
43855               }
43856             }
43857             return dialCode;
43858         },
43859         
43860         reset : function()
43861         {
43862             this.setValue(this.defaultDialCode);
43863             this.validate();
43864         },
43865         
43866         hiddenEl : function()
43867         {
43868             return this.el.select('input.hidden-tel-input',true).first();
43869         },
43870         
43871         // after setting val
43872         onKeyUp : function(e){
43873             this.setValue(this.getValue());
43874         },
43875         
43876         onKeyPress : function(e){
43877             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43878                 e.stopEvent();
43879             }
43880         }
43881         
43882 });
43883 /**
43884  * @class Roo.bootstrap.form.MoneyField
43885  * @extends Roo.bootstrap.form.ComboBox
43886  * Bootstrap MoneyField class
43887  * 
43888  * @constructor
43889  * Create a new MoneyField.
43890  * @param {Object} config Configuration options
43891  */
43892
43893 Roo.bootstrap.form.MoneyField = function(config) {
43894     
43895     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43896     
43897 };
43898
43899 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43900     
43901     /**
43902      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43903      */
43904     allowDecimals : true,
43905     /**
43906      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43907      */
43908     decimalSeparator : ".",
43909     /**
43910      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43911      */
43912     decimalPrecision : 0,
43913     /**
43914      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43915      */
43916     allowNegative : true,
43917     /**
43918      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43919      */
43920     allowZero: true,
43921     /**
43922      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43923      */
43924     minValue : Number.NEGATIVE_INFINITY,
43925     /**
43926      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43927      */
43928     maxValue : Number.MAX_VALUE,
43929     /**
43930      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43931      */
43932     minText : "The minimum value for this field is {0}",
43933     /**
43934      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43935      */
43936     maxText : "The maximum value for this field is {0}",
43937     /**
43938      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43939      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43940      */
43941     nanText : "{0} is not a valid number",
43942     /**
43943      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43944      */
43945     castInt : true,
43946     /**
43947      * @cfg {String} defaults currency of the MoneyField
43948      * value should be in lkey
43949      */
43950     defaultCurrency : false,
43951     /**
43952      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43953      */
43954     thousandsDelimiter : false,
43955     /**
43956      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43957      */
43958     max_length: false,
43959     
43960     inputlg : 9,
43961     inputmd : 9,
43962     inputsm : 9,
43963     inputxs : 6,
43964      /**
43965      * @cfg {Roo.data.Store} store  Store to lookup currency??
43966      */
43967     store : false,
43968     
43969     getAutoCreate : function()
43970     {
43971         var align = this.labelAlign || this.parentLabelAlign();
43972         
43973         var id = Roo.id();
43974
43975         var cfg = {
43976             cls: 'form-group',
43977             cn: []
43978         };
43979
43980         var input =  {
43981             tag: 'input',
43982             id : id,
43983             cls : 'form-control roo-money-amount-input',
43984             autocomplete: 'new-password'
43985         };
43986         
43987         var hiddenInput = {
43988             tag: 'input',
43989             type: 'hidden',
43990             id: Roo.id(),
43991             cls: 'hidden-number-input'
43992         };
43993         
43994         if(this.max_length) {
43995             input.maxlength = this.max_length; 
43996         }
43997         
43998         if (this.name) {
43999             hiddenInput.name = this.name;
44000         }
44001
44002         if (this.disabled) {
44003             input.disabled = true;
44004         }
44005
44006         var clg = 12 - this.inputlg;
44007         var cmd = 12 - this.inputmd;
44008         var csm = 12 - this.inputsm;
44009         var cxs = 12 - this.inputxs;
44010         
44011         var container = {
44012             tag : 'div',
44013             cls : 'row roo-money-field',
44014             cn : [
44015                 {
44016                     tag : 'div',
44017                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44018                     cn : [
44019                         {
44020                             tag : 'div',
44021                             cls: 'roo-select2-container input-group',
44022                             cn: [
44023                                 {
44024                                     tag : 'input',
44025                                     cls : 'form-control roo-money-currency-input',
44026                                     autocomplete: 'new-password',
44027                                     readOnly : 1,
44028                                     name : this.currencyName
44029                                 },
44030                                 {
44031                                     tag :'span',
44032                                     cls : 'input-group-addon',
44033                                     cn : [
44034                                         {
44035                                             tag: 'span',
44036                                             cls: 'caret'
44037                                         }
44038                                     ]
44039                                 }
44040                             ]
44041                         }
44042                     ]
44043                 },
44044                 {
44045                     tag : 'div',
44046                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44047                     cn : [
44048                         {
44049                             tag: 'div',
44050                             cls: this.hasFeedback ? 'has-feedback' : '',
44051                             cn: [
44052                                 input
44053                             ]
44054                         }
44055                     ]
44056                 }
44057             ]
44058             
44059         };
44060         
44061         if (this.fieldLabel.length) {
44062             var indicator = {
44063                 tag: 'i',
44064                 tooltip: 'This field is required'
44065             };
44066
44067             var label = {
44068                 tag: 'label',
44069                 'for':  id,
44070                 cls: 'control-label',
44071                 cn: []
44072             };
44073
44074             var label_text = {
44075                 tag: 'span',
44076                 html: this.fieldLabel
44077             };
44078
44079             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44080             label.cn = [
44081                 indicator,
44082                 label_text
44083             ];
44084
44085             if(this.indicatorpos == 'right') {
44086                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44087                 label.cn = [
44088                     label_text,
44089                     indicator
44090                 ];
44091             }
44092
44093             if(align == 'left') {
44094                 container = {
44095                     tag: 'div',
44096                     cn: [
44097                         container
44098                     ]
44099                 };
44100
44101                 if(this.labelWidth > 12){
44102                     label.style = "width: " + this.labelWidth + 'px';
44103                 }
44104                 if(this.labelWidth < 13 && this.labelmd == 0){
44105                     this.labelmd = this.labelWidth;
44106                 }
44107                 if(this.labellg > 0){
44108                     label.cls += ' col-lg-' + this.labellg;
44109                     input.cls += ' col-lg-' + (12 - this.labellg);
44110                 }
44111                 if(this.labelmd > 0){
44112                     label.cls += ' col-md-' + this.labelmd;
44113                     container.cls += ' col-md-' + (12 - this.labelmd);
44114                 }
44115                 if(this.labelsm > 0){
44116                     label.cls += ' col-sm-' + this.labelsm;
44117                     container.cls += ' col-sm-' + (12 - this.labelsm);
44118                 }
44119                 if(this.labelxs > 0){
44120                     label.cls += ' col-xs-' + this.labelxs;
44121                     container.cls += ' col-xs-' + (12 - this.labelxs);
44122                 }
44123             }
44124         }
44125
44126         cfg.cn = [
44127             label,
44128             container,
44129             hiddenInput
44130         ];
44131         
44132         var settings = this;
44133
44134         ['xs','sm','md','lg'].map(function(size){
44135             if (settings[size]) {
44136                 cfg.cls += ' col-' + size + '-' + settings[size];
44137             }
44138         });
44139         
44140         return cfg;
44141     },
44142     
44143     initEvents : function()
44144     {
44145         this.indicator = this.indicatorEl();
44146         
44147         this.initCurrencyEvent();
44148         
44149         this.initNumberEvent();
44150     },
44151     
44152     initCurrencyEvent : function()
44153     {
44154         if (!this.store) {
44155             throw "can not find store for combo";
44156         }
44157         
44158         this.store = Roo.factory(this.store, Roo.data);
44159         this.store.parent = this;
44160         
44161         this.createList();
44162         
44163         this.triggerEl = this.el.select('.input-group-addon', true).first();
44164         
44165         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44166         
44167         var _this = this;
44168         
44169         (function(){
44170             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44171             _this.list.setWidth(lw);
44172         }).defer(100);
44173         
44174         this.list.on('mouseover', this.onViewOver, this);
44175         this.list.on('mousemove', this.onViewMove, this);
44176         this.list.on('scroll', this.onViewScroll, this);
44177         
44178         if(!this.tpl){
44179             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44180         }
44181         
44182         this.view = new Roo.View(this.list, this.tpl, {
44183             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44184         });
44185         
44186         this.view.on('click', this.onViewClick, this);
44187         
44188         this.store.on('beforeload', this.onBeforeLoad, this);
44189         this.store.on('load', this.onLoad, this);
44190         this.store.on('loadexception', this.onLoadException, this);
44191         
44192         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44193             "up" : function(e){
44194                 this.inKeyMode = true;
44195                 this.selectPrev();
44196             },
44197
44198             "down" : function(e){
44199                 if(!this.isExpanded()){
44200                     this.onTriggerClick();
44201                 }else{
44202                     this.inKeyMode = true;
44203                     this.selectNext();
44204                 }
44205             },
44206
44207             "enter" : function(e){
44208                 this.collapse();
44209                 
44210                 if(this.fireEvent("specialkey", this, e)){
44211                     this.onViewClick(false);
44212                 }
44213                 
44214                 return true;
44215             },
44216
44217             "esc" : function(e){
44218                 this.collapse();
44219             },
44220
44221             "tab" : function(e){
44222                 this.collapse();
44223                 
44224                 if(this.fireEvent("specialkey", this, e)){
44225                     this.onViewClick(false);
44226                 }
44227                 
44228                 return true;
44229             },
44230
44231             scope : this,
44232
44233             doRelay : function(foo, bar, hname){
44234                 if(hname == 'down' || this.scope.isExpanded()){
44235                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44236                 }
44237                 return true;
44238             },
44239
44240             forceKeyDown: true
44241         });
44242         
44243         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44244         
44245     },
44246     
44247     initNumberEvent : function(e)
44248     {
44249         this.inputEl().on("keydown" , this.fireKey,  this);
44250         this.inputEl().on("focus", this.onFocus,  this);
44251         this.inputEl().on("blur", this.onBlur,  this);
44252         
44253         this.inputEl().relayEvent('keyup', this);
44254         
44255         if(this.indicator){
44256             this.indicator.addClass('invisible');
44257         }
44258  
44259         this.originalValue = this.getValue();
44260         
44261         if(this.validationEvent == 'keyup'){
44262             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44263             this.inputEl().on('keyup', this.filterValidation, this);
44264         }
44265         else if(this.validationEvent !== false){
44266             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44267         }
44268         
44269         if(this.selectOnFocus){
44270             this.on("focus", this.preFocus, this);
44271             
44272         }
44273         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44274             this.inputEl().on("keypress", this.filterKeys, this);
44275         } else {
44276             this.inputEl().relayEvent('keypress', this);
44277         }
44278         
44279         var allowed = "0123456789";
44280         
44281         if(this.allowDecimals){
44282             allowed += this.decimalSeparator;
44283         }
44284         
44285         if(this.allowNegative){
44286             allowed += "-";
44287         }
44288         
44289         if(this.thousandsDelimiter) {
44290             allowed += ",";
44291         }
44292         
44293         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44294         
44295         var keyPress = function(e){
44296             
44297             var k = e.getKey();
44298             
44299             var c = e.getCharCode();
44300             
44301             if(
44302                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44303                     allowed.indexOf(String.fromCharCode(c)) === -1
44304             ){
44305                 e.stopEvent();
44306                 return;
44307             }
44308             
44309             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44310                 return;
44311             }
44312             
44313             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44314                 e.stopEvent();
44315             }
44316         };
44317         
44318         this.inputEl().on("keypress", keyPress, this);
44319         
44320     },
44321     
44322     onTriggerClick : function(e)
44323     {   
44324         if(this.disabled){
44325             return;
44326         }
44327         
44328         this.page = 0;
44329         this.loadNext = false;
44330         
44331         if(this.isExpanded()){
44332             this.collapse();
44333             return;
44334         }
44335         
44336         this.hasFocus = true;
44337         
44338         if(this.triggerAction == 'all') {
44339             this.doQuery(this.allQuery, true);
44340             return;
44341         }
44342         
44343         this.doQuery(this.getRawValue());
44344     },
44345     
44346     getCurrency : function()
44347     {   
44348         var v = this.currencyEl().getValue();
44349         
44350         return v;
44351     },
44352     
44353     restrictHeight : function()
44354     {
44355         this.list.alignTo(this.currencyEl(), this.listAlign);
44356         this.list.alignTo(this.currencyEl(), this.listAlign);
44357     },
44358     
44359     onViewClick : function(view, doFocus, el, e)
44360     {
44361         var index = this.view.getSelectedIndexes()[0];
44362         
44363         var r = this.store.getAt(index);
44364         
44365         if(r){
44366             this.onSelect(r, index);
44367         }
44368     },
44369     
44370     onSelect : function(record, index){
44371         
44372         if(this.fireEvent('beforeselect', this, record, index) !== false){
44373         
44374             this.setFromCurrencyData(index > -1 ? record.data : false);
44375             
44376             this.collapse();
44377             
44378             this.fireEvent('select', this, record, index);
44379         }
44380     },
44381     
44382     setFromCurrencyData : function(o)
44383     {
44384         var currency = '';
44385         
44386         this.lastCurrency = o;
44387         
44388         if (this.currencyField) {
44389             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44390         } else {
44391             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44392         }
44393         
44394         this.lastSelectionText = currency;
44395         
44396         //setting default currency
44397         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44398             this.setCurrency(this.defaultCurrency);
44399             return;
44400         }
44401         
44402         this.setCurrency(currency);
44403     },
44404     
44405     setFromData : function(o)
44406     {
44407         var c = {};
44408         
44409         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44410         
44411         this.setFromCurrencyData(c);
44412         
44413         var value = '';
44414         
44415         if (this.name) {
44416             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44417         } else {
44418             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44419         }
44420         
44421         this.setValue(value);
44422         
44423     },
44424     
44425     setCurrency : function(v)
44426     {   
44427         this.currencyValue = v;
44428         
44429         if(this.rendered){
44430             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44431             this.validate();
44432         }
44433     },
44434     
44435     setValue : function(v)
44436     {
44437         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44438         
44439         this.value = v;
44440         
44441         if(this.rendered){
44442             
44443             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44444             
44445             this.inputEl().dom.value = (v == '') ? '' :
44446                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44447             
44448             if(!this.allowZero && v === '0') {
44449                 this.hiddenEl().dom.value = '';
44450                 this.inputEl().dom.value = '';
44451             }
44452             
44453             this.validate();
44454         }
44455     },
44456     
44457     getRawValue : function()
44458     {
44459         var v = this.inputEl().getValue();
44460         
44461         return v;
44462     },
44463     
44464     getValue : function()
44465     {
44466         return this.fixPrecision(this.parseValue(this.getRawValue()));
44467     },
44468     
44469     parseValue : function(value)
44470     {
44471         if(this.thousandsDelimiter) {
44472             value += "";
44473             r = new RegExp(",", "g");
44474             value = value.replace(r, "");
44475         }
44476         
44477         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44478         return isNaN(value) ? '' : value;
44479         
44480     },
44481     
44482     fixPrecision : function(value)
44483     {
44484         if(this.thousandsDelimiter) {
44485             value += "";
44486             r = new RegExp(",", "g");
44487             value = value.replace(r, "");
44488         }
44489         
44490         var nan = isNaN(value);
44491         
44492         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44493             return nan ? '' : value;
44494         }
44495         return parseFloat(value).toFixed(this.decimalPrecision);
44496     },
44497     
44498     decimalPrecisionFcn : function(v)
44499     {
44500         return Math.floor(v);
44501     },
44502     
44503     validateValue : function(value)
44504     {
44505         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44506             return false;
44507         }
44508         
44509         var num = this.parseValue(value);
44510         
44511         if(isNaN(num)){
44512             this.markInvalid(String.format(this.nanText, value));
44513             return false;
44514         }
44515         
44516         if(num < this.minValue){
44517             this.markInvalid(String.format(this.minText, this.minValue));
44518             return false;
44519         }
44520         
44521         if(num > this.maxValue){
44522             this.markInvalid(String.format(this.maxText, this.maxValue));
44523             return false;
44524         }
44525         
44526         return true;
44527     },
44528     
44529     validate : function()
44530     {
44531         if(this.disabled || this.allowBlank){
44532             this.markValid();
44533             return true;
44534         }
44535         
44536         var currency = this.getCurrency();
44537         
44538         if(this.validateValue(this.getRawValue()) && currency.length){
44539             this.markValid();
44540             return true;
44541         }
44542         
44543         this.markInvalid();
44544         return false;
44545     },
44546     
44547     getName: function()
44548     {
44549         return this.name;
44550     },
44551     
44552     beforeBlur : function()
44553     {
44554         if(!this.castInt){
44555             return;
44556         }
44557         
44558         var v = this.parseValue(this.getRawValue());
44559         
44560         if(v || v == 0){
44561             this.setValue(v);
44562         }
44563     },
44564     
44565     onBlur : function()
44566     {
44567         this.beforeBlur();
44568         
44569         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44570             //this.el.removeClass(this.focusClass);
44571         }
44572         
44573         this.hasFocus = false;
44574         
44575         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44576             this.validate();
44577         }
44578         
44579         var v = this.getValue();
44580         
44581         if(String(v) !== String(this.startValue)){
44582             this.fireEvent('change', this, v, this.startValue);
44583         }
44584         
44585         this.fireEvent("blur", this);
44586     },
44587     
44588     inputEl : function()
44589     {
44590         return this.el.select('.roo-money-amount-input', true).first();
44591     },
44592     
44593     currencyEl : function()
44594     {
44595         return this.el.select('.roo-money-currency-input', true).first();
44596     },
44597     
44598     hiddenEl : function()
44599     {
44600         return this.el.select('input.hidden-number-input',true).first();
44601     }
44602     
44603 });/**
44604  * @class Roo.bootstrap.BezierSignature
44605  * @extends Roo.bootstrap.Component
44606  * Bootstrap BezierSignature class
44607  * This script refer to:
44608  *    Title: Signature Pad
44609  *    Author: szimek
44610  *    Availability: https://github.com/szimek/signature_pad
44611  *
44612  * @constructor
44613  * Create a new BezierSignature
44614  * @param {Object} config The config object
44615  */
44616
44617 Roo.bootstrap.BezierSignature = function(config){
44618     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44619     this.addEvents({
44620         "resize" : true
44621     });
44622 };
44623
44624 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44625 {
44626      
44627     curve_data: [],
44628     
44629     is_empty: true,
44630     
44631     mouse_btn_down: true,
44632     
44633     /**
44634      * @cfg {int} canvas height
44635      */
44636     canvas_height: '200px',
44637     
44638     /**
44639      * @cfg {float|function} Radius of a single dot.
44640      */ 
44641     dot_size: false,
44642     
44643     /**
44644      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44645      */
44646     min_width: 0.5,
44647     
44648     /**
44649      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44650      */
44651     max_width: 2.5,
44652     
44653     /**
44654      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44655      */
44656     throttle: 16,
44657     
44658     /**
44659      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44660      */
44661     min_distance: 5,
44662     
44663     /**
44664      * @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.
44665      */
44666     bg_color: 'rgba(0, 0, 0, 0)',
44667     
44668     /**
44669      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44670      */
44671     dot_color: 'black',
44672     
44673     /**
44674      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44675      */ 
44676     velocity_filter_weight: 0.7,
44677     
44678     /**
44679      * @cfg {function} Callback when stroke begin. 
44680      */
44681     onBegin: false,
44682     
44683     /**
44684      * @cfg {function} Callback when stroke end.
44685      */
44686     onEnd: false,
44687     
44688     getAutoCreate : function()
44689     {
44690         var cls = 'roo-signature column';
44691         
44692         if(this.cls){
44693             cls += ' ' + this.cls;
44694         }
44695         
44696         var col_sizes = [
44697             'lg',
44698             'md',
44699             'sm',
44700             'xs'
44701         ];
44702         
44703         for(var i = 0; i < col_sizes.length; i++) {
44704             if(this[col_sizes[i]]) {
44705                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44706             }
44707         }
44708         
44709         var cfg = {
44710             tag: 'div',
44711             cls: cls,
44712             cn: [
44713                 {
44714                     tag: 'div',
44715                     cls: 'roo-signature-body',
44716                     cn: [
44717                         {
44718                             tag: 'canvas',
44719                             cls: 'roo-signature-body-canvas',
44720                             height: this.canvas_height,
44721                             width: this.canvas_width
44722                         }
44723                     ]
44724                 },
44725                 {
44726                     tag: 'input',
44727                     type: 'file',
44728                     style: 'display: none'
44729                 }
44730             ]
44731         };
44732         
44733         return cfg;
44734     },
44735     
44736     initEvents: function() 
44737     {
44738         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44739         
44740         var canvas = this.canvasEl();
44741         
44742         // mouse && touch event swapping...
44743         canvas.dom.style.touchAction = 'none';
44744         canvas.dom.style.msTouchAction = 'none';
44745         
44746         this.mouse_btn_down = false;
44747         canvas.on('mousedown', this._handleMouseDown, this);
44748         canvas.on('mousemove', this._handleMouseMove, this);
44749         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44750         
44751         if (window.PointerEvent) {
44752             canvas.on('pointerdown', this._handleMouseDown, this);
44753             canvas.on('pointermove', this._handleMouseMove, this);
44754             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44755         }
44756         
44757         if ('ontouchstart' in window) {
44758             canvas.on('touchstart', this._handleTouchStart, this);
44759             canvas.on('touchmove', this._handleTouchMove, this);
44760             canvas.on('touchend', this._handleTouchEnd, this);
44761         }
44762         
44763         Roo.EventManager.onWindowResize(this.resize, this, true);
44764         
44765         // file input event
44766         this.fileEl().on('change', this.uploadImage, this);
44767         
44768         this.clear();
44769         
44770         this.resize();
44771     },
44772     
44773     resize: function(){
44774         
44775         var canvas = this.canvasEl().dom;
44776         var ctx = this.canvasElCtx();
44777         var img_data = false;
44778         
44779         if(canvas.width > 0) {
44780             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44781         }
44782         // setting canvas width will clean img data
44783         canvas.width = 0;
44784         
44785         var style = window.getComputedStyle ? 
44786             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44787             
44788         var padding_left = parseInt(style.paddingLeft) || 0;
44789         var padding_right = parseInt(style.paddingRight) || 0;
44790         
44791         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44792         
44793         if(img_data) {
44794             ctx.putImageData(img_data, 0, 0);
44795         }
44796     },
44797     
44798     _handleMouseDown: function(e)
44799     {
44800         if (e.browserEvent.which === 1) {
44801             this.mouse_btn_down = true;
44802             this.strokeBegin(e);
44803         }
44804     },
44805     
44806     _handleMouseMove: function (e)
44807     {
44808         if (this.mouse_btn_down) {
44809             this.strokeMoveUpdate(e);
44810         }
44811     },
44812     
44813     _handleMouseUp: function (e)
44814     {
44815         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44816             this.mouse_btn_down = false;
44817             this.strokeEnd(e);
44818         }
44819     },
44820     
44821     _handleTouchStart: function (e) {
44822         
44823         e.preventDefault();
44824         if (e.browserEvent.targetTouches.length === 1) {
44825             // var touch = e.browserEvent.changedTouches[0];
44826             // this.strokeBegin(touch);
44827             
44828              this.strokeBegin(e); // assume e catching the correct xy...
44829         }
44830     },
44831     
44832     _handleTouchMove: function (e) {
44833         e.preventDefault();
44834         // var touch = event.targetTouches[0];
44835         // _this._strokeMoveUpdate(touch);
44836         this.strokeMoveUpdate(e);
44837     },
44838     
44839     _handleTouchEnd: function (e) {
44840         var wasCanvasTouched = e.target === this.canvasEl().dom;
44841         if (wasCanvasTouched) {
44842             e.preventDefault();
44843             // var touch = event.changedTouches[0];
44844             // _this._strokeEnd(touch);
44845             this.strokeEnd(e);
44846         }
44847     },
44848     
44849     reset: function () {
44850         this._lastPoints = [];
44851         this._lastVelocity = 0;
44852         this._lastWidth = (this.min_width + this.max_width) / 2;
44853         this.canvasElCtx().fillStyle = this.dot_color;
44854     },
44855     
44856     strokeMoveUpdate: function(e)
44857     {
44858         this.strokeUpdate(e);
44859         
44860         if (this.throttle) {
44861             this.throttleStroke(this.strokeUpdate, this.throttle);
44862         }
44863         else {
44864             this.strokeUpdate(e);
44865         }
44866     },
44867     
44868     strokeBegin: function(e)
44869     {
44870         var newPointGroup = {
44871             color: this.dot_color,
44872             points: []
44873         };
44874         
44875         if (typeof this.onBegin === 'function') {
44876             this.onBegin(e);
44877         }
44878         
44879         this.curve_data.push(newPointGroup);
44880         this.reset();
44881         this.strokeUpdate(e);
44882     },
44883     
44884     strokeUpdate: function(e)
44885     {
44886         var rect = this.canvasEl().dom.getBoundingClientRect();
44887         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44888         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44889         var lastPoints = lastPointGroup.points;
44890         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44891         var isLastPointTooClose = lastPoint
44892             ? point.distanceTo(lastPoint) <= this.min_distance
44893             : false;
44894         var color = lastPointGroup.color;
44895         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44896             var curve = this.addPoint(point);
44897             if (!lastPoint) {
44898                 this.drawDot({color: color, point: point});
44899             }
44900             else if (curve) {
44901                 this.drawCurve({color: color, curve: curve});
44902             }
44903             lastPoints.push({
44904                 time: point.time,
44905                 x: point.x,
44906                 y: point.y
44907             });
44908         }
44909     },
44910     
44911     strokeEnd: function(e)
44912     {
44913         this.strokeUpdate(e);
44914         if (typeof this.onEnd === 'function') {
44915             this.onEnd(e);
44916         }
44917     },
44918     
44919     addPoint:  function (point) {
44920         var _lastPoints = this._lastPoints;
44921         _lastPoints.push(point);
44922         if (_lastPoints.length > 2) {
44923             if (_lastPoints.length === 3) {
44924                 _lastPoints.unshift(_lastPoints[0]);
44925             }
44926             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44927             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44928             _lastPoints.shift();
44929             return curve;
44930         }
44931         return null;
44932     },
44933     
44934     calculateCurveWidths: function (startPoint, endPoint) {
44935         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44936             (1 - this.velocity_filter_weight) * this._lastVelocity;
44937
44938         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44939         var widths = {
44940             end: newWidth,
44941             start: this._lastWidth
44942         };
44943         
44944         this._lastVelocity = velocity;
44945         this._lastWidth = newWidth;
44946         return widths;
44947     },
44948     
44949     drawDot: function (_a) {
44950         var color = _a.color, point = _a.point;
44951         var ctx = this.canvasElCtx();
44952         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44953         ctx.beginPath();
44954         this.drawCurveSegment(point.x, point.y, width);
44955         ctx.closePath();
44956         ctx.fillStyle = color;
44957         ctx.fill();
44958     },
44959     
44960     drawCurve: function (_a) {
44961         var color = _a.color, curve = _a.curve;
44962         var ctx = this.canvasElCtx();
44963         var widthDelta = curve.endWidth - curve.startWidth;
44964         var drawSteps = Math.floor(curve.length()) * 2;
44965         ctx.beginPath();
44966         ctx.fillStyle = color;
44967         for (var i = 0; i < drawSteps; i += 1) {
44968         var t = i / drawSteps;
44969         var tt = t * t;
44970         var ttt = tt * t;
44971         var u = 1 - t;
44972         var uu = u * u;
44973         var uuu = uu * u;
44974         var x = uuu * curve.startPoint.x;
44975         x += 3 * uu * t * curve.control1.x;
44976         x += 3 * u * tt * curve.control2.x;
44977         x += ttt * curve.endPoint.x;
44978         var y = uuu * curve.startPoint.y;
44979         y += 3 * uu * t * curve.control1.y;
44980         y += 3 * u * tt * curve.control2.y;
44981         y += ttt * curve.endPoint.y;
44982         var width = curve.startWidth + ttt * widthDelta;
44983         this.drawCurveSegment(x, y, width);
44984         }
44985         ctx.closePath();
44986         ctx.fill();
44987     },
44988     
44989     drawCurveSegment: function (x, y, width) {
44990         var ctx = this.canvasElCtx();
44991         ctx.moveTo(x, y);
44992         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44993         this.is_empty = false;
44994     },
44995     
44996     clear: function()
44997     {
44998         var ctx = this.canvasElCtx();
44999         var canvas = this.canvasEl().dom;
45000         ctx.fillStyle = this.bg_color;
45001         ctx.clearRect(0, 0, canvas.width, canvas.height);
45002         ctx.fillRect(0, 0, canvas.width, canvas.height);
45003         this.curve_data = [];
45004         this.reset();
45005         this.is_empty = true;
45006     },
45007     
45008     fileEl: function()
45009     {
45010         return  this.el.select('input',true).first();
45011     },
45012     
45013     canvasEl: function()
45014     {
45015         return this.el.select('canvas',true).first();
45016     },
45017     
45018     canvasElCtx: function()
45019     {
45020         return this.el.select('canvas',true).first().dom.getContext('2d');
45021     },
45022     
45023     getImage: function(type)
45024     {
45025         if(this.is_empty) {
45026             return false;
45027         }
45028         
45029         // encryption ?
45030         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45031     },
45032     
45033     drawFromImage: function(img_src)
45034     {
45035         var img = new Image();
45036         
45037         img.onload = function(){
45038             this.canvasElCtx().drawImage(img, 0, 0);
45039         }.bind(this);
45040         
45041         img.src = img_src;
45042         
45043         this.is_empty = false;
45044     },
45045     
45046     selectImage: function()
45047     {
45048         this.fileEl().dom.click();
45049     },
45050     
45051     uploadImage: function(e)
45052     {
45053         var reader = new FileReader();
45054         
45055         reader.onload = function(e){
45056             var img = new Image();
45057             img.onload = function(){
45058                 this.reset();
45059                 this.canvasElCtx().drawImage(img, 0, 0);
45060             }.bind(this);
45061             img.src = e.target.result;
45062         }.bind(this);
45063         
45064         reader.readAsDataURL(e.target.files[0]);
45065     },
45066     
45067     // Bezier Point Constructor
45068     Point: (function () {
45069         function Point(x, y, time) {
45070             this.x = x;
45071             this.y = y;
45072             this.time = time || Date.now();
45073         }
45074         Point.prototype.distanceTo = function (start) {
45075             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45076         };
45077         Point.prototype.equals = function (other) {
45078             return this.x === other.x && this.y === other.y && this.time === other.time;
45079         };
45080         Point.prototype.velocityFrom = function (start) {
45081             return this.time !== start.time
45082             ? this.distanceTo(start) / (this.time - start.time)
45083             : 0;
45084         };
45085         return Point;
45086     }()),
45087     
45088     
45089     // Bezier Constructor
45090     Bezier: (function () {
45091         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45092             this.startPoint = startPoint;
45093             this.control2 = control2;
45094             this.control1 = control1;
45095             this.endPoint = endPoint;
45096             this.startWidth = startWidth;
45097             this.endWidth = endWidth;
45098         }
45099         Bezier.fromPoints = function (points, widths, scope) {
45100             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45101             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45102             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45103         };
45104         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45105             var dx1 = s1.x - s2.x;
45106             var dy1 = s1.y - s2.y;
45107             var dx2 = s2.x - s3.x;
45108             var dy2 = s2.y - s3.y;
45109             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45110             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45111             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45112             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45113             var dxm = m1.x - m2.x;
45114             var dym = m1.y - m2.y;
45115             var k = l2 / (l1 + l2);
45116             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45117             var tx = s2.x - cm.x;
45118             var ty = s2.y - cm.y;
45119             return {
45120                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45121                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45122             };
45123         };
45124         Bezier.prototype.length = function () {
45125             var steps = 10;
45126             var length = 0;
45127             var px;
45128             var py;
45129             for (var i = 0; i <= steps; i += 1) {
45130                 var t = i / steps;
45131                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45132                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45133                 if (i > 0) {
45134                     var xdiff = cx - px;
45135                     var ydiff = cy - py;
45136                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45137                 }
45138                 px = cx;
45139                 py = cy;
45140             }
45141             return length;
45142         };
45143         Bezier.prototype.point = function (t, start, c1, c2, end) {
45144             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45145             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45146             + (3.0 * c2 * (1.0 - t) * t * t)
45147             + (end * t * t * t);
45148         };
45149         return Bezier;
45150     }()),
45151     
45152     throttleStroke: function(fn, wait) {
45153       if (wait === void 0) { wait = 250; }
45154       var previous = 0;
45155       var timeout = null;
45156       var result;
45157       var storedContext;
45158       var storedArgs;
45159       var later = function () {
45160           previous = Date.now();
45161           timeout = null;
45162           result = fn.apply(storedContext, storedArgs);
45163           if (!timeout) {
45164               storedContext = null;
45165               storedArgs = [];
45166           }
45167       };
45168       return function wrapper() {
45169           var args = [];
45170           for (var _i = 0; _i < arguments.length; _i++) {
45171               args[_i] = arguments[_i];
45172           }
45173           var now = Date.now();
45174           var remaining = wait - (now - previous);
45175           storedContext = this;
45176           storedArgs = args;
45177           if (remaining <= 0 || remaining > wait) {
45178               if (timeout) {
45179                   clearTimeout(timeout);
45180                   timeout = null;
45181               }
45182               previous = now;
45183               result = fn.apply(storedContext, storedArgs);
45184               if (!timeout) {
45185                   storedContext = null;
45186                   storedArgs = [];
45187               }
45188           }
45189           else if (!timeout) {
45190               timeout = window.setTimeout(later, remaining);
45191           }
45192           return result;
45193       };
45194   }
45195   
45196 });
45197
45198  
45199
45200  // old names for form elements
45201 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
45202 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
45203 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
45204 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
45205 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
45206 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
45207 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
45208 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
45209 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
45210 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
45211 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
45212 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
45213 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
45214 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
45215 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
45216 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
45217 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
45218 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
45219 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
45220 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
45221 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
45222 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
45223 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
45224 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
45225 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
45226 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
45227
45228 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
45229 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45230
45231 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
45232 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
45233
45234 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
45235 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45236 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
45237 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
45238