58be41bb8c2b52c27264a530e9974b7c0f5219de
[roojs1] / roojs-bootstrap-debug.js
1 Roo.bootstrap = {};/**
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        
2939         
2940         return  {
2941             cls :'div' ,
2942             cn : [
2943                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this) 
2944             ]
2945         };
2946            
2947          
2948     },
2949      
2950    
2951     initEvents : function()
2952     {
2953         
2954         Roo.bootstrap.Button.prototype.initEvents.call(this);
2955         
2956         
2957         
2958         
2959         
2960         this.urlAPI = (window.createObjectURL && window) || 
2961                                 (window.URL && URL.revokeObjectURL && URL) || 
2962                                 (window.webkitURL && webkitURL);
2963                         
2964         var im = {
2965             tag: 'input',
2966             type : 'file',
2967             cls : 'd-none  roo-card-upload-selector' 
2968           
2969         };
2970         if (this.multiple) {
2971             im.multiple = 'multiple';
2972         }
2973         this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2974        
2975         //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2976         
2977         this.selectorEl.on('change', this.onFileSelected, this);
2978          
2979          
2980        
2981     },
2982     
2983    
2984     onClick : function(e)
2985     {
2986         e.preventDefault();
2987         
2988         if ( this.fireEvent('beforeselect', this) === false) {
2989             return;
2990         }
2991          
2992         this.selectorEl.dom.click();
2993          
2994     },
2995     
2996     onFileSelected : function(e)
2997     {
2998         e.preventDefault();
2999         
3000         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3001             return;
3002         }
3003         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004         this.selectorEl.dom.value  = '';// hopefully reset..
3005         
3006         this.fireEvent('uploaded', this,  files );
3007         
3008     },
3009     
3010        
3011    
3012     
3013     /**
3014      * addCard - add an Attachment to the uploader
3015      * @param data - the data about the image to upload
3016      *
3017      * {
3018           id : 123
3019           title : "Title of file",
3020           is_uploaded : false,
3021           src : "http://.....",
3022           srcfile : { the File upload object },
3023           mimetype : file.type,
3024           preview : false,
3025           is_deleted : 0
3026           .. any other data...
3027         }
3028      *
3029      * 
3030     */
3031      
3032     reset: function()
3033     {
3034          
3035          this.selectorEl
3036     } 
3037     
3038     
3039     
3040     
3041 });
3042  /*
3043  * - LGPL
3044  *
3045  * image
3046  * 
3047  */
3048
3049
3050 /**
3051  * @class Roo.bootstrap.Img
3052  * @extends Roo.bootstrap.Component
3053  * Bootstrap Img class
3054  * @cfg {Boolean} imgResponsive false | true
3055  * @cfg {String} border rounded | circle | thumbnail
3056  * @cfg {String} src image source
3057  * @cfg {String} alt image alternative text
3058  * @cfg {String} href a tag href
3059  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060  * @cfg {String} xsUrl xs image source
3061  * @cfg {String} smUrl sm image source
3062  * @cfg {String} mdUrl md image source
3063  * @cfg {String} lgUrl lg image source
3064  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3065  * 
3066  * @constructor
3067  * Create a new Input
3068  * @param {Object} config The config object
3069  */
3070
3071 Roo.bootstrap.Img = function(config){
3072     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3073     
3074     this.addEvents({
3075         // img events
3076         /**
3077          * @event click
3078          * The img click event for the img.
3079          * @param {Roo.EventObject} e
3080          */
3081         "click" : true,
3082         /**
3083          * @event load
3084          * The when any image loads
3085          * @param {Roo.EventObject} e
3086          */
3087         "load" : true
3088     });
3089 };
3090
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3092     
3093     imgResponsive: true,
3094     border: '',
3095     src: 'about:blank',
3096     href: false,
3097     target: false,
3098     xsUrl: '',
3099     smUrl: '',
3100     mdUrl: '',
3101     lgUrl: '',
3102     backgroundContain : false,
3103
3104     getAutoCreate : function()
3105     {   
3106         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107             return this.createSingleImg();
3108         }
3109         
3110         var cfg = {
3111             tag: 'div',
3112             cls: 'roo-image-responsive-group',
3113             cn: []
3114         };
3115         var _this = this;
3116         
3117         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3118             
3119             if(!_this[size + 'Url']){
3120                 return;
3121             }
3122             
3123             var img = {
3124                 tag: 'img',
3125                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126                 html: _this.html || cfg.html,
3127                 src: _this[size + 'Url']
3128             };
3129             
3130             img.cls += ' roo-image-responsive-' + size;
3131             
3132             var s = ['xs', 'sm', 'md', 'lg'];
3133             
3134             s.splice(s.indexOf(size), 1);
3135             
3136             Roo.each(s, function(ss){
3137                 img.cls += ' hidden-' + ss;
3138             });
3139             
3140             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141                 cfg.cls += ' img-' + _this.border;
3142             }
3143             
3144             if(_this.alt){
3145                 cfg.alt = _this.alt;
3146             }
3147             
3148             if(_this.href){
3149                 var a = {
3150                     tag: 'a',
3151                     href: _this.href,
3152                     cn: [
3153                         img
3154                     ]
3155                 };
3156
3157                 if(this.target){
3158                     a.target = _this.target;
3159                 }
3160             }
3161             
3162             cfg.cn.push((_this.href) ? a : img);
3163             
3164         });
3165         
3166         return cfg;
3167     },
3168     
3169     createSingleImg : function()
3170     {
3171         var cfg = {
3172             tag: 'img',
3173             cls: (this.imgResponsive) ? 'img-responsive' : '',
3174             html : null,
3175             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3176         };
3177         
3178         if (this.backgroundContain) {
3179             cfg.cls += ' background-contain';
3180         }
3181         
3182         cfg.html = this.html || cfg.html;
3183         
3184         if (this.backgroundContain) {
3185             cfg.style="background-image: url(" + this.src + ')';
3186         } else {
3187             cfg.src = this.src || cfg.src;
3188         }
3189         
3190         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191             cfg.cls += ' img-' + this.border;
3192         }
3193         
3194         if(this.alt){
3195             cfg.alt = this.alt;
3196         }
3197         
3198         if(this.href){
3199             var a = {
3200                 tag: 'a',
3201                 href: this.href,
3202                 cn: [
3203                     cfg
3204                 ]
3205             };
3206             
3207             if(this.target){
3208                 a.target = this.target;
3209             }
3210             
3211         }
3212         
3213         return (this.href) ? a : cfg;
3214     },
3215     
3216     initEvents: function() 
3217     {
3218         if(!this.href){
3219             this.el.on('click', this.onClick, this);
3220         }
3221         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222             this.el.on('load', this.onImageLoad, this);
3223         } else {
3224             // not sure if this works.. not tested
3225             this.el.select('img', true).on('load', this.onImageLoad, this);
3226         }
3227         
3228     },
3229     
3230     onClick : function(e)
3231     {
3232         Roo.log('img onclick');
3233         this.fireEvent('click', this, e);
3234     },
3235     onImageLoad: function(e)
3236     {
3237         Roo.log('img load');
3238         this.fireEvent('load', this, e);
3239     },
3240     
3241     /**
3242      * Sets the url of the image - used to update it
3243      * @param {String} url the url of the image
3244      */
3245     
3246     setSrc : function(url)
3247     {
3248         this.src =  url;
3249         
3250         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251             if (this.backgroundContain) {
3252                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3253             } else {
3254                 this.el.dom.src =  url;
3255             }
3256             return;
3257         }
3258         
3259         this.el.select('img', true).first().dom.src =  url;
3260     }
3261     
3262     
3263    
3264 });
3265
3266  /*
3267  * - LGPL
3268  *
3269  * image
3270  * 
3271  */
3272
3273
3274 /**
3275  * @class Roo.bootstrap.Link
3276  * @extends Roo.bootstrap.Component
3277  * @children Roo.bootstrap.Component
3278  * Bootstrap Link Class (eg. '<a href>')
3279  
3280  * @cfg {String} alt image alternative text
3281  * @cfg {String} href a tag href
3282  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283  * @cfg {String} html the content of the link.
3284  * @cfg {String} anchor name for the anchor link
3285  * @cfg {String} fa - favicon
3286
3287  * @cfg {Boolean} preventDefault (true | false) default false
3288
3289  * 
3290  * @constructor
3291  * Create a new Input
3292  * @param {Object} config The config object
3293  */
3294
3295 Roo.bootstrap.Link = function(config){
3296     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3297     
3298     this.addEvents({
3299         // img events
3300         /**
3301          * @event click
3302          * The img click event for the img.
3303          * @param {Roo.EventObject} e
3304          */
3305         "click" : true
3306     });
3307 };
3308
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3310     
3311     href: false,
3312     target: false,
3313     preventDefault: false,
3314     anchor : false,
3315     alt : false,
3316     fa: false,
3317
3318
3319     getAutoCreate : function()
3320     {
3321         var html = this.html || '';
3322         
3323         if (this.fa !== false) {
3324             html = '<i class="fa fa-' + this.fa + '"></i>';
3325         }
3326         var cfg = {
3327             tag: 'a'
3328         };
3329         // anchor's do not require html/href...
3330         if (this.anchor === false) {
3331             cfg.html = html;
3332             cfg.href = this.href || '#';
3333         } else {
3334             cfg.name = this.anchor;
3335             if (this.html !== false || this.fa !== false) {
3336                 cfg.html = html;
3337             }
3338             if (this.href !== false) {
3339                 cfg.href = this.href;
3340             }
3341         }
3342         
3343         if(this.alt !== false){
3344             cfg.alt = this.alt;
3345         }
3346         
3347         
3348         if(this.target !== false) {
3349             cfg.target = this.target;
3350         }
3351         
3352         return cfg;
3353     },
3354     
3355     initEvents: function() {
3356         
3357         if(!this.href || this.preventDefault){
3358             this.el.on('click', this.onClick, this);
3359         }
3360     },
3361     
3362     onClick : function(e)
3363     {
3364         if(this.preventDefault){
3365             e.preventDefault();
3366         }
3367         //Roo.log('img onclick');
3368         this.fireEvent('click', this, e);
3369     }
3370    
3371 });
3372
3373  /*
3374  * - LGPL
3375  *
3376  * header
3377  * 
3378  */
3379
3380 /**
3381  * @class Roo.bootstrap.Header
3382  * @extends Roo.bootstrap.Component
3383  * @children Roo.bootstrap.Component
3384  * Bootstrap Header class
3385  *
3386  * 
3387  * @cfg {String} html content of header
3388  * @cfg {Number} level (1|2|3|4|5|6) default 1
3389  * 
3390  * @constructor
3391  * Create a new Header
3392  * @param {Object} config The config object
3393  */
3394
3395
3396 Roo.bootstrap.Header  = function(config){
3397     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3398 };
3399
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3401     
3402     //href : false,
3403     html : false,
3404     level : 1,
3405     
3406     
3407     
3408     getAutoCreate : function(){
3409         
3410         
3411         
3412         var cfg = {
3413             tag: 'h' + (1 *this.level),
3414             html: this.html || ''
3415         } ;
3416         
3417         return cfg;
3418     }
3419    
3420 });
3421
3422  
3423
3424  /**
3425  * @class Roo.bootstrap.MenuMgr
3426  * @licence LGPL
3427  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3428  * @static
3429  */
3430 Roo.bootstrap.menu.Manager = function(){
3431    var menus, active, groups = {}, attached = false, lastShow = new Date();
3432
3433    // private - called when first menu is created
3434    function init(){
3435        menus = {};
3436        active = new Roo.util.MixedCollection();
3437        Roo.get(document).addKeyListener(27, function(){
3438            if(active.length > 0){
3439                hideAll();
3440            }
3441        });
3442    }
3443
3444    // private
3445    function hideAll(){
3446        if(active && active.length > 0){
3447            var c = active.clone();
3448            c.each(function(m){
3449                m.hide();
3450            });
3451        }
3452    }
3453
3454    // private
3455    function onHide(m){
3456        active.remove(m);
3457        if(active.length < 1){
3458            Roo.get(document).un("mouseup", onMouseDown);
3459             
3460            attached = false;
3461        }
3462    }
3463
3464    // private
3465    function onShow(m){
3466        var last = active.last();
3467        lastShow = new Date();
3468        active.add(m);
3469        if(!attached){
3470           Roo.get(document).on("mouseup", onMouseDown);
3471            
3472            attached = true;
3473        }
3474        if(m.parentMenu){
3475           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476           m.parentMenu.activeChild = m;
3477        }else if(last && last.isVisible()){
3478           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3479        }
3480    }
3481
3482    // private
3483    function onBeforeHide(m){
3484        if(m.activeChild){
3485            m.activeChild.hide();
3486        }
3487        if(m.autoHideTimer){
3488            clearTimeout(m.autoHideTimer);
3489            delete m.autoHideTimer;
3490        }
3491    }
3492
3493    // private
3494    function onBeforeShow(m){
3495        var pm = m.parentMenu;
3496        if(!pm && !m.allowOtherMenus){
3497            hideAll();
3498        }else if(pm && pm.activeChild && active != m){
3499            pm.activeChild.hide();
3500        }
3501    }
3502
3503    // private this should really trigger on mouseup..
3504    function onMouseDown(e){
3505         Roo.log("on Mouse Up");
3506         
3507         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508             Roo.log("MenuManager hideAll");
3509             hideAll();
3510             e.stopEvent();
3511         }
3512         
3513         
3514    }
3515
3516    // private
3517    function onBeforeCheck(mi, state){
3518        if(state){
3519            var g = groups[mi.group];
3520            for(var i = 0, l = g.length; i < l; i++){
3521                if(g[i] != mi){
3522                    g[i].setChecked(false);
3523                }
3524            }
3525        }
3526    }
3527
3528    return {
3529
3530        /**
3531         * Hides all menus that are currently visible
3532         */
3533        hideAll : function(){
3534             hideAll();  
3535        },
3536
3537        // private
3538        register : function(menu){
3539            if(!menus){
3540                init();
3541            }
3542            menus[menu.id] = menu;
3543            menu.on("beforehide", onBeforeHide);
3544            menu.on("hide", onHide);
3545            menu.on("beforeshow", onBeforeShow);
3546            menu.on("show", onShow);
3547            var g = menu.group;
3548            if(g && menu.events["checkchange"]){
3549                if(!groups[g]){
3550                    groups[g] = [];
3551                }
3552                groups[g].push(menu);
3553                menu.on("checkchange", onCheck);
3554            }
3555        },
3556
3557         /**
3558          * Returns a {@link Roo.menu.Menu} object
3559          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560          * be used to generate and return a new Menu instance.
3561          */
3562        get : function(menu){
3563            if(typeof menu == "string"){ // menu id
3564                return menus[menu];
3565            }else if(menu.events){  // menu instance
3566                return menu;
3567            }
3568            /*else if(typeof menu.length == 'number'){ // array of menu items?
3569                return new Roo.bootstrap.Menu({items:menu});
3570            }else{ // otherwise, must be a config
3571                return new Roo.bootstrap.Menu(menu);
3572            }
3573            */
3574            return false;
3575        },
3576
3577        // private
3578        unregister : function(menu){
3579            delete menus[menu.id];
3580            menu.un("beforehide", onBeforeHide);
3581            menu.un("hide", onHide);
3582            menu.un("beforeshow", onBeforeShow);
3583            menu.un("show", onShow);
3584            var g = menu.group;
3585            if(g && menu.events["checkchange"]){
3586                groups[g].remove(menu);
3587                menu.un("checkchange", onCheck);
3588            }
3589        },
3590
3591        // private
3592        registerCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                if(!groups[g]){
3596                    groups[g] = [];
3597                }
3598                groups[g].push(menuItem);
3599                menuItem.on("beforecheckchange", onBeforeCheck);
3600            }
3601        },
3602
3603        // private
3604        unregisterCheckable : function(menuItem){
3605            var g = menuItem.group;
3606            if(g){
3607                groups[g].remove(menuItem);
3608                menuItem.un("beforecheckchange", onBeforeCheck);
3609            }
3610        }
3611    };
3612 }(); 
3613 /**
3614  * @class Roo.bootstrap.menu.Menu
3615  * @extends Roo.bootstrap.Component
3616  * @licence LGPL
3617  * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3618  * @parent none
3619  * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3620  * 
3621  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3623  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3624  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3626 * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3627  
3628  * @constructor
3629  * Create a new Menu
3630  * @param {Object} config The config objectQ
3631  */
3632
3633
3634 Roo.bootstrap.menu.Menu = function(config){
3635     
3636     if (config.type == 'treeview') {
3637         // normally menu's are drawn attached to the document to handle layering etc..
3638         // however treeview (used by the docs menu is drawn into the parent element)
3639         this.container_method = 'getChildContainer'; 
3640     }
3641     
3642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643     if (this.registerMenu && this.type != 'treeview')  {
3644         Roo.bootstrap.menu.Manager.register(this);
3645     }
3646     
3647     
3648     this.addEvents({
3649         /**
3650          * @event beforeshow
3651          * Fires before this menu is displayed (return false to block)
3652          * @param {Roo.menu.Menu} this
3653          */
3654         beforeshow : true,
3655         /**
3656          * @event beforehide
3657          * Fires before this menu is hidden (return false to block)
3658          * @param {Roo.menu.Menu} this
3659          */
3660         beforehide : true,
3661         /**
3662          * @event show
3663          * Fires after this menu is displayed
3664          * @param {Roo.menu.Menu} this
3665          */
3666         show : true,
3667         /**
3668          * @event hide
3669          * Fires after this menu is hidden
3670          * @param {Roo.menu.Menu} this
3671          */
3672         hide : true,
3673         /**
3674          * @event click
3675          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676          * @param {Roo.menu.Menu} this
3677          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678          * @param {Roo.EventObject} e
3679          */
3680         click : true,
3681         /**
3682          * @event mouseover
3683          * Fires when the mouse is hovering over this menu
3684          * @param {Roo.menu.Menu} this
3685          * @param {Roo.EventObject} e
3686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687          */
3688         mouseover : true,
3689         /**
3690          * @event mouseout
3691          * Fires when the mouse exits this menu
3692          * @param {Roo.menu.Menu} this
3693          * @param {Roo.EventObject} e
3694          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3695          */
3696         mouseout : true,
3697         /**
3698          * @event itemclick
3699          * Fires when a menu item contained in this menu is clicked
3700          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701          * @param {Roo.EventObject} e
3702          */
3703         itemclick: true
3704     });
3705     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3706 };
3707
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
3709     
3710    /// html : false,
3711    
3712     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3713     type: false,
3714     /**
3715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3716      */
3717     registerMenu : true,
3718     
3719     menuItems :false, // stores the menu items..
3720     
3721     hidden:true,
3722         
3723     parentMenu : false,
3724     
3725     stopEvent : true,
3726     
3727     isLink : false,
3728     
3729     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3730     
3731     hideTrigger : false,
3732     
3733     align : 'tl-bl?',
3734     
3735     
3736     getChildContainer : function() {
3737         return this.el;  
3738     },
3739     
3740     getAutoCreate : function(){
3741          
3742         //if (['right'].indexOf(this.align)!==-1) {
3743         //    cfg.cn[1].cls += ' pull-right'
3744         //}
3745          
3746         var cfg = {
3747             tag : 'ul',
3748             cls : 'dropdown-menu shadow' ,
3749             style : 'z-index:1000'
3750             
3751         };
3752         
3753         if (this.type === 'submenu') {
3754             cfg.cls = 'submenu active';
3755         }
3756         if (this.type === 'treeview') {
3757             cfg.cls = 'treeview-menu';
3758         }
3759         
3760         return cfg;
3761     },
3762     initEvents : function() {
3763         
3764        // Roo.log("ADD event");
3765        // Roo.log(this.triggerEl.dom);
3766         if (this.triggerEl) {
3767             
3768             this.triggerEl.on('click', this.onTriggerClick, this);
3769             
3770             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3771             
3772             if (!this.hideTrigger) {
3773                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774                     // dropdown toggle on the 'a' in BS4?
3775                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3776                 } else {
3777                     this.triggerEl.addClass('dropdown-toggle');
3778                 }
3779             }
3780         }
3781         
3782         if (Roo.isTouch) {
3783             this.el.on('touchstart'  , this.onTouch, this);
3784         }
3785         this.el.on('click' , this.onClick, this);
3786
3787         this.el.on("mouseover", this.onMouseOver, this);
3788         this.el.on("mouseout", this.onMouseOut, this);
3789         
3790     },
3791     
3792     findTargetItem : function(e)
3793     {
3794         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3795         if(!t){
3796             return false;
3797         }
3798         //Roo.log(t);         Roo.log(t.id);
3799         if(t && t.id){
3800             //Roo.log(this.menuitems);
3801             return this.menuitems.get(t.id);
3802             
3803             //return this.items.get(t.menuItemId);
3804         }
3805         
3806         return false;
3807     },
3808     
3809     onTouch : function(e) 
3810     {
3811         Roo.log("menu.onTouch");
3812         //e.stopEvent(); this make the user popdown broken
3813         this.onClick(e);
3814     },
3815     
3816     onClick : function(e)
3817     {
3818         Roo.log("menu.onClick");
3819         
3820         var t = this.findTargetItem(e);
3821         if(!t || t.isContainer){
3822             return;
3823         }
3824         Roo.log(e);
3825         /*
3826         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3827             if(t == this.activeItem && t.shouldDeactivate(e)){
3828                 this.activeItem.deactivate();
3829                 delete this.activeItem;
3830                 return;
3831             }
3832             if(t.canActivate){
3833                 this.setActiveItem(t, true);
3834             }
3835             return;
3836             
3837             
3838         }
3839         */
3840        
3841         Roo.log('pass click event');
3842         
3843         t.onClick(e);
3844         
3845         this.fireEvent("click", this, t, e);
3846         
3847         var _this = this;
3848         
3849         if(!t.href.length || t.href == '#'){
3850             (function() { _this.hide(); }).defer(100);
3851         }
3852         
3853     },
3854     
3855     onMouseOver : function(e){
3856         var t  = this.findTargetItem(e);
3857         //Roo.log(t);
3858         //if(t){
3859         //    if(t.canActivate && !t.disabled){
3860         //        this.setActiveItem(t, true);
3861         //    }
3862         //}
3863         
3864         this.fireEvent("mouseover", this, e, t);
3865     },
3866     isVisible : function(){
3867         return !this.hidden;
3868     },
3869     onMouseOut : function(e){
3870         var t  = this.findTargetItem(e);
3871         
3872         //if(t ){
3873         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3874         //        this.activeItem.deactivate();
3875         //        delete this.activeItem;
3876         //    }
3877         //}
3878         this.fireEvent("mouseout", this, e, t);
3879     },
3880     
3881     
3882     /**
3883      * Displays this menu relative to another element
3884      * @param {String/HTMLElement/Roo.Element} element The element to align to
3885      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886      * the element (defaults to this.defaultAlign)
3887      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3888      */
3889     show : function(el, pos, parentMenu)
3890     {
3891         if (false === this.fireEvent("beforeshow", this)) {
3892             Roo.log("show canceled");
3893             return;
3894         }
3895         this.parentMenu = parentMenu;
3896         if(!this.el){
3897             this.render();
3898         }
3899         this.el.addClass('show'); // show otherwise we do not know how big we are..
3900          
3901         var xy = this.el.getAlignToXY(el, pos);
3902         
3903         // bl-tl << left align  below
3904         // tl-bl << left align 
3905         
3906         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907             // if it goes to far to the right.. -> align left.
3908             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3909         }
3910         if(xy[0] < 0){
3911             // was left align - go right?
3912             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3913         }
3914         
3915         // goes down the bottom
3916         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3917            xy[1]  < 0 ){
3918             var a = this.align.replace('?', '').split('-');
3919             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3920             
3921         }
3922         
3923         this.showAt(  xy , parentMenu, false);
3924     },
3925      /**
3926      * Displays this menu at a specific xy position
3927      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3929      */
3930     showAt : function(xy, parentMenu, /* private: */_e){
3931         this.parentMenu = parentMenu;
3932         if(!this.el){
3933             this.render();
3934         }
3935         if(_e !== false){
3936             this.fireEvent("beforeshow", this);
3937             //xy = this.el.adjustForConstraints(xy);
3938         }
3939         
3940         //this.el.show();
3941         this.hideMenuItems();
3942         this.hidden = false;
3943         if (this.triggerEl) {
3944             this.triggerEl.addClass('open');
3945         }
3946         
3947         this.el.addClass('show');
3948         
3949         
3950         
3951         // reassign x when hitting right
3952         
3953         // reassign y when hitting bottom
3954         
3955         // but the list may align on trigger left or trigger top... should it be a properity?
3956         
3957         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3958             this.el.setXY(xy);
3959         }
3960         
3961         this.focus();
3962         this.fireEvent("show", this);
3963     },
3964     
3965     focus : function(){
3966         return;
3967         if(!this.hidden){
3968             this.doFocus.defer(50, this);
3969         }
3970     },
3971
3972     doFocus : function(){
3973         if(!this.hidden){
3974             this.focusEl.focus();
3975         }
3976     },
3977
3978     /**
3979      * Hides this menu and optionally all parent menus
3980      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3981      */
3982     hide : function(deep)
3983     {
3984         if (false === this.fireEvent("beforehide", this)) {
3985             Roo.log("hide canceled");
3986             return;
3987         }
3988         this.hideMenuItems();
3989         if(this.el && this.isVisible()){
3990            
3991             if(this.activeItem){
3992                 this.activeItem.deactivate();
3993                 this.activeItem = null;
3994             }
3995             if (this.triggerEl) {
3996                 this.triggerEl.removeClass('open');
3997             }
3998             
3999             this.el.removeClass('show');
4000             this.hidden = true;
4001             this.fireEvent("hide", this);
4002         }
4003         if(deep === true && this.parentMenu){
4004             this.parentMenu.hide(true);
4005         }
4006     },
4007     
4008     onTriggerClick : function(e)
4009     {
4010         Roo.log('trigger click');
4011         
4012         var target = e.getTarget();
4013         
4014         Roo.log(target.nodeName.toLowerCase());
4015         
4016         if(target.nodeName.toLowerCase() === 'i'){
4017             e.preventDefault();
4018         }
4019         
4020     },
4021     
4022     onTriggerPress  : function(e)
4023     {
4024         Roo.log('trigger press');
4025         //Roo.log(e.getTarget());
4026        // Roo.log(this.triggerEl.dom);
4027        
4028         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029         var pel = Roo.get(e.getTarget());
4030         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031             Roo.log('is treeview or dropdown?');
4032             return;
4033         }
4034         
4035         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4036             return;
4037         }
4038         
4039         if (this.isVisible()) {
4040             Roo.log('hide');
4041             this.hide();
4042         } else {
4043             Roo.log('show');
4044             
4045             this.show(this.triggerEl, this.align, false);
4046         }
4047         
4048         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4049             e.stopEvent();
4050         }
4051         
4052     },
4053        
4054     
4055     hideMenuItems : function()
4056     {
4057         Roo.log("hide Menu Items");
4058         if (!this.el) { 
4059             return;
4060         }
4061         
4062         this.el.select('.open',true).each(function(aa) {
4063             
4064             aa.removeClass('open');
4065          
4066         });
4067     },
4068     addxtypeChild : function (tree, cntr) {
4069         var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4070           
4071         this.menuitems.add(comp);
4072         return comp;
4073
4074     },
4075     getEl : function()
4076     {
4077         Roo.log(this.el);
4078         return this.el;
4079     },
4080     
4081     clear : function()
4082     {
4083         this.getEl().dom.innerHTML = '';
4084         this.menuitems.clear();
4085     }
4086 });
4087
4088  
4089  /**
4090  * @class Roo.bootstrap.menu.Item
4091  * @extends Roo.bootstrap.Component
4092  * @children  Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093  * @parent Roo.bootstrap.menu.Menu
4094  * @licence LGPL
4095  * Bootstrap MenuItem class
4096  * 
4097  * @cfg {String} html the menu label
4098  * @cfg {String} href the link
4099  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4102  * @cfg {String} fa favicon to show on left of menu item.
4103  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4104  * 
4105  * 
4106  * @constructor
4107  * Create a new MenuItem
4108  * @param {Object} config The config object
4109  */
4110
4111
4112 Roo.bootstrap.menu.Item = function(config){
4113     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4114     this.addEvents({
4115         // raw events
4116         /**
4117          * @event click
4118          * The raw click event for the entire grid.
4119          * @param {Roo.bootstrap.menu.Item} this
4120          * @param {Roo.EventObject} e
4121          */
4122         "click" : true
4123     });
4124 };
4125
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
4127     
4128     href : false,
4129     html : false,
4130     preventDefault: false,
4131     isContainer : false,
4132     active : false,
4133     fa: false,
4134     
4135     getAutoCreate : function(){
4136         
4137         if(this.isContainer){
4138             return {
4139                 tag: 'li',
4140                 cls: 'dropdown-menu-item '
4141             };
4142         }
4143         var ctag = {
4144             tag: 'span',
4145             html: 'Link'
4146         };
4147         
4148         var anc = {
4149             tag : 'a',
4150             cls : 'dropdown-item',
4151             href : '#',
4152             cn : [  ]
4153         };
4154         
4155         if (this.fa !== false) {
4156             anc.cn.push({
4157                 tag : 'i',
4158                 cls : 'fa fa-' + this.fa
4159             });
4160         }
4161         
4162         anc.cn.push(ctag);
4163         
4164         
4165         var cfg= {
4166             tag: 'li',
4167             cls: 'dropdown-menu-item',
4168             cn: [ anc ]
4169         };
4170         if (this.parent().type == 'treeview') {
4171             cfg.cls = 'treeview-menu';
4172         }
4173         if (this.active) {
4174             cfg.cls += ' active';
4175         }
4176         
4177         
4178         
4179         anc.href = this.href || cfg.cn[0].href ;
4180         ctag.html = this.html || cfg.cn[0].html ;
4181         return cfg;
4182     },
4183     
4184     initEvents: function()
4185     {
4186         if (this.parent().type == 'treeview') {
4187             this.el.select('a').on('click', this.onClick, this);
4188         }
4189         
4190         if (this.menu) {
4191             this.menu.parentType = this.xtype;
4192             this.menu.triggerEl = this.el;
4193             this.menu = this.addxtype(Roo.apply({}, this.menu));
4194         }
4195         
4196     },
4197     onClick : function(e)
4198     {
4199         //Roo.log('item on click ');
4200         
4201         if(this.href === false || this.preventDefault){
4202             e.preventDefault();
4203         }
4204         //this.parent().hideMenuItems();
4205         
4206         this.fireEvent('click', this, e);
4207     },
4208     getEl : function()
4209     {
4210         return this.el;
4211     } 
4212 });
4213
4214  
4215
4216  
4217
4218   
4219 /**
4220  * @class Roo.bootstrap.menu.Separator
4221  * @extends Roo.bootstrap.Component
4222  * @licence LGPL
4223  * @parent Roo.bootstrap.menu.Menu
4224  * Bootstrap Separator class
4225  * 
4226  * @constructor
4227  * Create a new Separator
4228  * @param {Object} config The config object
4229  */
4230
4231
4232 Roo.bootstrap.menu.Separator = function(config){
4233     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4234 };
4235
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
4237     
4238     getAutoCreate : function(){
4239         var cfg = {
4240             tag : 'li',
4241             cls: 'dropdown-divider divider'
4242         };
4243         
4244         return cfg;
4245     }
4246    
4247 });
4248
4249  
4250
4251  
4252 /*
4253 * Licence: LGPL
4254 */
4255
4256 /**
4257  * @class Roo.bootstrap.Modal
4258  * @extends Roo.bootstrap.Component
4259  * @parent none builder
4260  * @children Roo.bootstrap.Component
4261  * Bootstrap Modal class
4262  * @cfg {String} title Title of dialog
4263  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4265  * @cfg {Boolean} specificTitle default false
4266  * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268  * @cfg {Boolean} animate default true
4269  * @cfg {Boolean} allow_close default true
4270  * @cfg {Boolean} fitwindow default false
4271  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274  * @cfg {String} size (sm|lg|xl) default empty
4275  * @cfg {Number} max_width set the max width of modal
4276  * @cfg {Boolean} editableTitle can the title be edited
4277
4278  *
4279  *
4280  * @constructor
4281  * Create a new Modal Dialog
4282  * @param {Object} config The config object
4283  */
4284
4285 Roo.bootstrap.Modal = function(config){
4286     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4287     this.addEvents({
4288         // raw events
4289         /**
4290          * @event btnclick
4291          * The raw btnclick event for the button
4292          * @param {Roo.EventObject} e
4293          */
4294         "btnclick" : true,
4295         /**
4296          * @event resize
4297          * Fire when dialog resize
4298          * @param {Roo.bootstrap.Modal} this
4299          * @param {Roo.EventObject} e
4300          */
4301         "resize" : true,
4302         /**
4303          * @event titlechanged
4304          * Fire when the editable title has been changed
4305          * @param {Roo.bootstrap.Modal} this
4306          * @param {Roo.EventObject} value
4307          */
4308         "titlechanged" : true 
4309         
4310     });
4311     this.buttons = this.buttons || [];
4312
4313     if (this.tmpl) {
4314         this.tmpl = Roo.factory(this.tmpl);
4315     }
4316
4317 };
4318
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4320
4321     title : 'test dialog',
4322
4323     buttons : false,
4324
4325     // set on load...
4326
4327     html: false,
4328
4329     tmp: false,
4330
4331     specificTitle: false,
4332
4333     buttonPosition: 'right',
4334
4335     allow_close : true,
4336
4337     animate : true,
4338
4339     fitwindow: false,
4340     
4341      // private
4342     dialogEl: false,
4343     bodyEl:  false,
4344     footerEl:  false,
4345     titleEl:  false,
4346     closeEl:  false,
4347
4348     size: '',
4349     
4350     max_width: 0,
4351     
4352     max_height: 0,
4353     
4354     fit_content: false,
4355     editableTitle  : false,
4356
4357     onRender : function(ct, position)
4358     {
4359         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4360
4361         if(!this.el){
4362             var cfg = Roo.apply({},  this.getAutoCreate());
4363             cfg.id = Roo.id();
4364             //if(!cfg.name){
4365             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4366             //}
4367             //if (!cfg.name.length) {
4368             //    delete cfg.name;
4369            // }
4370             if (this.cls) {
4371                 cfg.cls += ' ' + this.cls;
4372             }
4373             if (this.style) {
4374                 cfg.style = this.style;
4375             }
4376             this.el = Roo.get(document.body).createChild(cfg, position);
4377         }
4378         //var type = this.el.dom.type;
4379
4380
4381         if(this.tabIndex !== undefined){
4382             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4383         }
4384
4385         this.dialogEl = this.el.select('.modal-dialog',true).first();
4386         this.bodyEl = this.el.select('.modal-body',true).first();
4387         this.closeEl = this.el.select('.modal-header .close', true).first();
4388         this.headerEl = this.el.select('.modal-header',true).first();
4389         this.titleEl = this.el.select('.modal-title',true).first();
4390         this.footerEl = this.el.select('.modal-footer',true).first();
4391
4392         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4393         
4394         //this.el.addClass("x-dlg-modal");
4395
4396         if (this.buttons.length) {
4397             Roo.each(this.buttons, function(bb) {
4398                 var b = Roo.apply({}, bb);
4399                 b.xns = b.xns || Roo.bootstrap;
4400                 b.xtype = b.xtype || 'Button';
4401                 if (typeof(b.listeners) == 'undefined') {
4402                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4403                 }
4404
4405                 var btn = Roo.factory(b);
4406
4407                 btn.render(this.getButtonContainer());
4408
4409             },this);
4410         }
4411         // render the children.
4412         var nitems = [];
4413
4414         if(typeof(this.items) != 'undefined'){
4415             var items = this.items;
4416             delete this.items;
4417
4418             for(var i =0;i < items.length;i++) {
4419                 // we force children not to montor widnow resize  - as we do that for them.
4420                 items[i].monitorWindowResize = false;
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         // any layout/border etc.. resize..
4629         (function () {
4630             this.items.forEach( function(e) {
4631                 e.layout ? e.layout() : false;
4632
4633             });
4634         }).defer(100,this);
4635         
4636     },
4637
4638     show : function() {
4639
4640         if (!this.rendered) {
4641             this.render();
4642         }
4643         this.toggleHeaderInput(false);
4644         //this.el.setStyle('display', 'block');
4645         this.el.removeClass('hideing');
4646         this.el.dom.style.display='block';
4647         
4648         Roo.get(document.body).addClass('modal-open');
4649  
4650         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4651             
4652             (function(){
4653                 this.el.addClass('show');
4654                 this.el.addClass('in');
4655             }).defer(50, this);
4656         }else{
4657             this.el.addClass('show');
4658             this.el.addClass('in');
4659         }
4660
4661         // not sure how we can show data in here..
4662         //if (this.tmpl) {
4663         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4664         //}
4665
4666         Roo.get(document.body).addClass("x-body-masked");
4667         
4668         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4669         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670         this.maskEl.dom.style.display = 'block';
4671         this.maskEl.addClass('show');
4672         
4673         
4674         this.resize();
4675         
4676         this.fireEvent('show', this);
4677
4678         // set zindex here - otherwise it appears to be ignored...
4679         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4680         
4681         
4682         // this is for children that are... layout.Border 
4683         (function () {
4684             this.items.forEach( function(e) {
4685                 e.layout ? e.layout() : false;
4686
4687             });
4688         }).defer(100,this);
4689
4690     },
4691     hide : function()
4692     {
4693         if(this.fireEvent("beforehide", this) !== false){
4694             
4695             this.maskEl.removeClass('show');
4696             
4697             this.maskEl.dom.style.display = '';
4698             Roo.get(document.body).removeClass("x-body-masked");
4699             this.el.removeClass('in');
4700             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4701
4702             if(this.animate){ // why
4703                 this.el.addClass('hideing');
4704                 this.el.removeClass('show');
4705                 (function(){
4706                     if (!this.el.hasClass('hideing')) {
4707                         return; // it's been shown again...
4708                     }
4709                     
4710                     this.el.dom.style.display='';
4711
4712                     Roo.get(document.body).removeClass('modal-open');
4713                     this.el.removeClass('hideing');
4714                 }).defer(150,this);
4715                 
4716             }else{
4717                 this.el.removeClass('show');
4718                 this.el.dom.style.display='';
4719                 Roo.get(document.body).removeClass('modal-open');
4720
4721             }
4722             this.fireEvent('hide', this);
4723         }
4724     },
4725     isVisible : function()
4726     {
4727         
4728         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4729         
4730     },
4731
4732     addButton : function(str, cb)
4733     {
4734
4735
4736         var b = Roo.apply({}, { html : str } );
4737         b.xns = b.xns || Roo.bootstrap;
4738         b.xtype = b.xtype || 'Button';
4739         if (typeof(b.listeners) == 'undefined') {
4740             b.listeners = { click : cb.createDelegate(this)  };
4741         }
4742
4743         var btn = Roo.factory(b);
4744
4745         btn.render(this.getButtonContainer());
4746
4747         return btn;
4748
4749     },
4750
4751     setDefaultButton : function(btn)
4752     {
4753         //this.el.select('.modal-footer').()
4754     },
4755
4756     resizeTo: function(w,h)
4757     {
4758         this.dialogEl.setWidth(w);
4759         
4760         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4761
4762         this.bodyEl.setHeight(h - diff);
4763         
4764         this.fireEvent('resize', this);
4765     },
4766     
4767     setContentSize  : function(w, h)
4768     {
4769
4770     },
4771     onButtonClick: function(btn,e)
4772     {
4773         //Roo.log([a,b,c]);
4774         this.fireEvent('btnclick', btn.name, e);
4775     },
4776      /**
4777      * Set the title of the Dialog
4778      * @param {String} str new Title
4779      */
4780     setTitle: function(str) {
4781         this.titleEl.dom.innerHTML = str;
4782         this.title = str;
4783     },
4784     /**
4785      * Set the body of the Dialog
4786      * @param {String} str new Title
4787      */
4788     setBody: function(str) {
4789         this.bodyEl.dom.innerHTML = str;
4790     },
4791     /**
4792      * Set the body of the Dialog using the template
4793      * @param {Obj} data - apply this data to the template and replace the body contents.
4794      */
4795     applyBody: function(obj)
4796     {
4797         if (!this.tmpl) {
4798             Roo.log("Error - using apply Body without a template");
4799             //code
4800         }
4801         this.tmpl.overwrite(this.bodyEl, obj);
4802     },
4803     
4804     getChildHeight : function(child_nodes)
4805     {
4806         if(
4807             !child_nodes ||
4808             child_nodes.length == 0
4809         ) {
4810             return 0;
4811         }
4812         
4813         var child_height = 0;
4814         
4815         for(var i = 0; i < child_nodes.length; i++) {
4816             
4817             /*
4818             * for modal with tabs...
4819             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4820                 
4821                 var layout_childs = child_nodes[i].childNodes;
4822                 
4823                 for(var j = 0; j < layout_childs.length; j++) {
4824                     
4825                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4826                         
4827                         var layout_body_childs = layout_childs[j].childNodes;
4828                         
4829                         for(var k = 0; k < layout_body_childs.length; k++) {
4830                             
4831                             if(layout_body_childs[k].classList.contains('navbar')) {
4832                                 child_height += layout_body_childs[k].offsetHeight;
4833                                 continue;
4834                             }
4835                             
4836                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4837                                 
4838                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4839                                 
4840                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4841                                     
4842                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4844                                         continue;
4845                                     }
4846                                     
4847                                 }
4848                                 
4849                             }
4850                             
4851                         }
4852                     }
4853                 }
4854                 continue;
4855             }
4856             */
4857             
4858             child_height += child_nodes[i].offsetHeight;
4859             // Roo.log(child_nodes[i].offsetHeight);
4860         }
4861         
4862         return child_height;
4863     },
4864     toggleHeaderInput : function(is_edit)
4865     {
4866         if (!this.editableTitle) {
4867             return; // not editable.
4868         }
4869         if (is_edit && this.is_header_editing) {
4870             return; // already editing..
4871         }
4872         if (is_edit) {
4873     
4874             this.headerEditEl.dom.value = this.title;
4875             this.headerEditEl.removeClass('d-none');
4876             this.headerEditEl.dom.focus();
4877             this.titleEl.addClass('d-none');
4878             
4879             this.is_header_editing = true;
4880             return
4881         }
4882         // flip back to not editing.
4883         this.title = this.headerEditEl.dom.value;
4884         this.headerEditEl.addClass('d-none');
4885         this.titleEl.removeClass('d-none');
4886         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887         this.is_header_editing = false;
4888         this.fireEvent('titlechanged', this, this.title);
4889     
4890             
4891         
4892     }
4893
4894 });
4895
4896
4897 Roo.apply(Roo.bootstrap.Modal,  {
4898     /**
4899          * Button config that displays a single OK button
4900          * @type Object
4901          */
4902         OK :  [{
4903             name : 'ok',
4904             weight : 'primary',
4905             html : 'OK'
4906         }],
4907         /**
4908          * Button config that displays Yes and No buttons
4909          * @type Object
4910          */
4911         YESNO : [
4912             {
4913                 name  : 'no',
4914                 html : 'No'
4915             },
4916             {
4917                 name  :'yes',
4918                 weight : 'primary',
4919                 html : 'Yes'
4920             }
4921         ],
4922
4923         /**
4924          * Button config that displays OK and Cancel buttons
4925          * @type Object
4926          */
4927         OKCANCEL : [
4928             {
4929                name : 'cancel',
4930                 html : 'Cancel'
4931             },
4932             {
4933                 name : 'ok',
4934                 weight : 'primary',
4935                 html : 'OK'
4936             }
4937         ],
4938         /**
4939          * Button config that displays Yes, No and Cancel buttons
4940          * @type Object
4941          */
4942         YESNOCANCEL : [
4943             {
4944                 name : 'yes',
4945                 weight : 'primary',
4946                 html : 'Yes'
4947             },
4948             {
4949                 name : 'no',
4950                 html : 'No'
4951             },
4952             {
4953                 name : 'cancel',
4954                 html : 'Cancel'
4955             }
4956         ],
4957         
4958         zIndex : 10001
4959 });
4960
4961 /*
4962  * - LGPL
4963  *
4964  * messagebox - can be used as a replace
4965  * 
4966  */
4967 /**
4968  * @class Roo.MessageBox
4969  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4970  * Example usage:
4971  *<pre><code>
4972 // Basic alert:
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4974
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4977     if (btn == 'ok'){
4978         // process text value...
4979     }
4980 });
4981
4982 // Show a dialog using config options:
4983 Roo.Msg.show({
4984    title:'Save Changes?',
4985    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986    buttons: Roo.Msg.YESNOCANCEL,
4987    fn: processResult,
4988    animEl: 'elId'
4989 });
4990 </code></pre>
4991  * @static
4992  */
4993 Roo.bootstrap.MessageBox = function(){
4994     var dlg, opt, mask, waitTimer;
4995     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996     var buttons, activeTextEl, bwidth;
4997
4998     
4999     // private
5000     var handleButton = function(button){
5001         dlg.hide();
5002         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5003     };
5004
5005     // private
5006     var handleHide = function(){
5007         if(opt && opt.cls){
5008             dlg.el.removeClass(opt.cls);
5009         }
5010         //if(waitTimer){
5011         //    Roo.TaskMgr.stop(waitTimer);
5012         //    waitTimer = null;
5013         //}
5014     };
5015
5016     // private
5017     var updateButtons = function(b){
5018         var width = 0;
5019         if(!b){
5020             buttons["ok"].hide();
5021             buttons["cancel"].hide();
5022             buttons["yes"].hide();
5023             buttons["no"].hide();
5024             dlg.footerEl.hide();
5025             
5026             return width;
5027         }
5028         dlg.footerEl.show();
5029         for(var k in buttons){
5030             if(typeof buttons[k] != "function"){
5031                 if(b[k]){
5032                     buttons[k].show();
5033                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034                     width += buttons[k].el.getWidth()+15;
5035                 }else{
5036                     buttons[k].hide();
5037                 }
5038             }
5039         }
5040         return width;
5041     };
5042
5043     // private
5044     var handleEsc = function(d, k, e){
5045         if(opt && opt.closable !== false){
5046             dlg.hide();
5047         }
5048         if(e){
5049             e.stopEvent();
5050         }
5051     };
5052
5053     return {
5054         /**
5055          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056          * @return {Roo.BasicDialog} The BasicDialog element
5057          */
5058         getDialog : function(){
5059            if(!dlg){
5060                 dlg = new Roo.bootstrap.Modal( {
5061                     //draggable: true,
5062                     //resizable:false,
5063                     //constraintoviewport:false,
5064                     //fixedcenter:true,
5065                     //collapsible : false,
5066                     //shim:true,
5067                     //modal: true,
5068                 //    width: 'auto',
5069                   //  height:100,
5070                     //buttonAlign:"center",
5071                     closeClick : function(){
5072                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5073                             handleButton("no");
5074                         }else{
5075                             handleButton("cancel");
5076                         }
5077                     }
5078                 });
5079                 dlg.render();
5080                 dlg.on("hide", handleHide);
5081                 mask = dlg.mask;
5082                 //dlg.addKeyListener(27, handleEsc);
5083                 buttons = {};
5084                 this.buttons = buttons;
5085                 var bt = this.buttonText;
5086                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5090                 //Roo.log(buttons);
5091                 bodyEl = dlg.bodyEl.createChild({
5092
5093                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094                         '<textarea class="roo-mb-textarea"></textarea>' +
5095                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5096                 });
5097                 msgEl = bodyEl.dom.firstChild;
5098                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099                 textboxEl.enableDisplayMode();
5100                 textboxEl.addKeyListener([10,13], function(){
5101                     if(dlg.isVisible() && opt && opt.buttons){
5102                         if(opt.buttons.ok){
5103                             handleButton("ok");
5104                         }else if(opt.buttons.yes){
5105                             handleButton("yes");
5106                         }
5107                     }
5108                 });
5109                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110                 textareaEl.enableDisplayMode();
5111                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112                 progressEl.enableDisplayMode();
5113                 
5114                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115                 var pf = progressEl.dom.firstChild;
5116                 if (pf) {
5117                     pp = Roo.get(pf.firstChild);
5118                     pp.setHeight(pf.offsetHeight);
5119                 }
5120                 
5121             }
5122             return dlg;
5123         },
5124
5125         /**
5126          * Updates the message box body text
5127          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128          * the XHTML-compliant non-breaking space character '&amp;#160;')
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateText : function(text)
5132         {
5133             if(!dlg.isVisible() && !opt.width){
5134                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5136             }
5137             msgEl.innerHTML = text || '&#160;';
5138       
5139             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5141             var w = Math.max(
5142                     Math.min(opt.width || cw , this.maxWidth), 
5143                     Math.max(opt.minWidth || this.minWidth, bwidth)
5144             );
5145             if(opt.prompt){
5146                 activeTextEl.setWidth(w);
5147             }
5148             if(dlg.isVisible()){
5149                 dlg.fixedcenter = false;
5150             }
5151             // to big, make it scroll. = But as usual stupid IE does not support
5152             // !important..
5153             
5154             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5157             } else {
5158                 bodyEl.dom.style.height = '';
5159                 bodyEl.dom.style.overflowY = '';
5160             }
5161             if (cw > w) {
5162                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5163             } else {
5164                 bodyEl.dom.style.overflowX = '';
5165             }
5166             
5167             dlg.setContentSize(w, bodyEl.getHeight());
5168             if(dlg.isVisible()){
5169                 dlg.fixedcenter = true;
5170             }
5171             return this;
5172         },
5173
5174         /**
5175          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5176          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179          * @return {Roo.MessageBox} This message box
5180          */
5181         updateProgress : function(value, text){
5182             if(text){
5183                 this.updateText(text);
5184             }
5185             
5186             if (pp) { // weird bug on my firefox - for some reason this is not defined
5187                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5189             }
5190             return this;
5191         },        
5192
5193         /**
5194          * Returns true if the message box is currently displayed
5195          * @return {Boolean} True if the message box is visible, else false
5196          */
5197         isVisible : function(){
5198             return dlg && dlg.isVisible();  
5199         },
5200
5201         /**
5202          * Hides the message box if it is displayed
5203          */
5204         hide : function(){
5205             if(this.isVisible()){
5206                 dlg.hide();
5207             }  
5208         },
5209
5210         /**
5211          * Displays a new message box, or reinitializes an existing message box, based on the config options
5212          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213          * The following config object properties are supported:
5214          * <pre>
5215 Property    Type             Description
5216 ----------  ---------------  ------------------------------------------------------------------------------------
5217 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5218                                    closes (defaults to undefined)
5219 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5222                                    progress and wait dialogs will ignore this property and always hide the
5223                                    close button as they can only be closed programmatically.
5224 cls               String           A custom CSS class to apply to the message box element
5225 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5226                                    displayed (defaults to 75)
5227 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5228                                    function will be btn (the name of the button that was clicked, if applicable,
5229                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5230                                    Progress and wait dialogs will ignore this option since they do not respond to
5231                                    user actions and can only be closed programmatically, so any required function
5232                                    should be called by the same code after it closes the dialog.
5233 icon              String           A CSS class that provides a background image to be used as an icon for
5234                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5236 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5237 modal             Boolean          False to allow user interaction with the page while the message box is
5238                                    displayed (defaults to true)
5239 msg               String           A string that will replace the existing message box body text (defaults
5240                                    to the XHTML-compliant non-breaking space character '&#160;')
5241 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5242 progress          Boolean          True to display a progress bar (defaults to false)
5243 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5246 title             String           The title text
5247 value             String           The string value to set into the active textbox element if displayed
5248 wait              Boolean          True to display a progress bar (defaults to false)
5249 width             Number           The width of the dialog in pixels
5250 </pre>
5251          *
5252          * Example usage:
5253          * <pre><code>
5254 Roo.Msg.show({
5255    title: 'Address',
5256    msg: 'Please enter your address:',
5257    width: 300,
5258    buttons: Roo.MessageBox.OKCANCEL,
5259    multiline: true,
5260    fn: saveAddress,
5261    animEl: 'addAddressBtn'
5262 });
5263 </code></pre>
5264          * @param {Object} config Configuration options
5265          * @return {Roo.MessageBox} This message box
5266          */
5267         show : function(options)
5268         {
5269             
5270             // this causes nightmares if you show one dialog after another
5271             // especially on callbacks..
5272              
5273             if(this.isVisible()){
5274                 
5275                 this.hide();
5276                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5278                 Roo.log("New Dialog Message:" +  options.msg )
5279                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5281                 
5282             }
5283             var d = this.getDialog();
5284             opt = options;
5285             d.setTitle(opt.title || "&#160;");
5286             d.closeEl.setDisplayed(opt.closable !== false);
5287             activeTextEl = textboxEl;
5288             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5289             if(opt.prompt){
5290                 if(opt.multiline){
5291                     textboxEl.hide();
5292                     textareaEl.show();
5293                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5294                         opt.multiline : this.defaultTextHeight);
5295                     activeTextEl = textareaEl;
5296                 }else{
5297                     textboxEl.show();
5298                     textareaEl.hide();
5299                 }
5300             }else{
5301                 textboxEl.hide();
5302                 textareaEl.hide();
5303             }
5304             progressEl.setDisplayed(opt.progress === true);
5305             if (opt.progress) {
5306                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5307             }
5308             this.updateProgress(0);
5309             activeTextEl.dom.value = opt.value || "";
5310             if(opt.prompt){
5311                 dlg.setDefaultButton(activeTextEl);
5312             }else{
5313                 var bs = opt.buttons;
5314                 var db = null;
5315                 if(bs && bs.ok){
5316                     db = buttons["ok"];
5317                 }else if(bs && bs.yes){
5318                     db = buttons["yes"];
5319                 }
5320                 dlg.setDefaultButton(db);
5321             }
5322             bwidth = updateButtons(opt.buttons);
5323             this.updateText(opt.msg);
5324             if(opt.cls){
5325                 d.el.addClass(opt.cls);
5326             }
5327             d.proxyDrag = opt.proxyDrag === true;
5328             d.modal = opt.modal !== false;
5329             d.mask = opt.modal !== false ? mask : false;
5330             if(!d.isVisible()){
5331                 // force it to the end of the z-index stack so it gets a cursor in FF
5332                 document.body.appendChild(dlg.el.dom);
5333                 d.animateTarget = null;
5334                 d.show(options.animEl);
5335             }
5336             return this;
5337         },
5338
5339         /**
5340          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5341          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342          * and closing the message box when the process is complete.
5343          * @param {String} title The title bar text
5344          * @param {String} msg The message box body text
5345          * @return {Roo.MessageBox} This message box
5346          */
5347         progress : function(title, msg){
5348             this.show({
5349                 title : title,
5350                 msg : msg,
5351                 buttons: false,
5352                 progress:true,
5353                 closable:false,
5354                 minWidth: this.minProgressWidth,
5355                 modal : true
5356             });
5357             return this;
5358         },
5359
5360         /**
5361          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362          * If a callback function is passed it will be called after the user clicks the button, and the
5363          * id of the button that was clicked will be passed as the only parameter to the callback
5364          * (could also be the top-right close button).
5365          * @param {String} title The title bar text
5366          * @param {String} msg The message box body text
5367          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368          * @param {Object} scope (optional) The scope of the callback function
5369          * @return {Roo.MessageBox} This message box
5370          */
5371         alert : function(title, msg, fn, scope)
5372         {
5373             this.show({
5374                 title : title,
5375                 msg : msg,
5376                 buttons: this.OK,
5377                 fn: fn,
5378                 closable : false,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5387          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388          * You are responsible for closing the message box when the process is complete.
5389          * @param {String} msg The message box body text
5390          * @param {String} title (optional) The title bar text
5391          * @return {Roo.MessageBox} This message box
5392          */
5393         wait : function(msg, title){
5394             this.show({
5395                 title : title,
5396                 msg : msg,
5397                 buttons: false,
5398                 closable:false,
5399                 progress:true,
5400                 modal:true,
5401                 width:300,
5402                 wait:true
5403             });
5404             waitTimer = Roo.TaskMgr.start({
5405                 run: function(i){
5406                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5407                 },
5408                 interval: 1000
5409             });
5410             return this;
5411         },
5412
5413         /**
5414          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417          * @param {String} title The title bar text
5418          * @param {String} msg The message box body text
5419          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420          * @param {Object} scope (optional) The scope of the callback function
5421          * @return {Roo.MessageBox} This message box
5422          */
5423         confirm : function(title, msg, fn, scope){
5424             this.show({
5425                 title : title,
5426                 msg : msg,
5427                 buttons: this.YESNO,
5428                 fn: fn,
5429                 scope : scope,
5430                 modal : true
5431             });
5432             return this;
5433         },
5434
5435         /**
5436          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5438          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439          * (could also be the top-right close button) and the text that was entered will be passed as the two
5440          * parameters to the callback.
5441          * @param {String} title The title bar text
5442          * @param {String} msg The message box body text
5443          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444          * @param {Object} scope (optional) The scope of the callback function
5445          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447          * @return {Roo.MessageBox} This message box
5448          */
5449         prompt : function(title, msg, fn, scope, multiline){
5450             this.show({
5451                 title : title,
5452                 msg : msg,
5453                 buttons: this.OKCANCEL,
5454                 fn: fn,
5455                 minWidth:250,
5456                 scope : scope,
5457                 prompt:true,
5458                 multiline: multiline,
5459                 modal : true
5460             });
5461             return this;
5462         },
5463
5464         /**
5465          * Button config that displays a single OK button
5466          * @type Object
5467          */
5468         OK : {ok:true},
5469         /**
5470          * Button config that displays Yes and No buttons
5471          * @type Object
5472          */
5473         YESNO : {yes:true, no:true},
5474         /**
5475          * Button config that displays OK and Cancel buttons
5476          * @type Object
5477          */
5478         OKCANCEL : {ok:true, cancel:true},
5479         /**
5480          * Button config that displays Yes, No and Cancel buttons
5481          * @type Object
5482          */
5483         YESNOCANCEL : {yes:true, no:true, cancel:true},
5484
5485         /**
5486          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5487          * @type Number
5488          */
5489         defaultTextHeight : 75,
5490         /**
5491          * The maximum width in pixels of the message box (defaults to 600)
5492          * @type Number
5493          */
5494         maxWidth : 600,
5495         /**
5496          * The minimum width in pixels of the message box (defaults to 100)
5497          * @type Number
5498          */
5499         minWidth : 100,
5500         /**
5501          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5502          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5503          * @type Number
5504          */
5505         minProgressWidth : 250,
5506         /**
5507          * An object containing the default button text strings that can be overriden for localized language support.
5508          * Supported properties are: ok, cancel, yes and no.
5509          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5510          * @type Object
5511          */
5512         buttonText : {
5513             ok : "OK",
5514             cancel : "Cancel",
5515             yes : "Yes",
5516             no : "No"
5517         }
5518     };
5519 }();
5520
5521 /**
5522  * Shorthand for {@link Roo.MessageBox}
5523  */
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5526 /*
5527  * - LGPL
5528  *
5529  * navbar
5530  * 
5531  */
5532
5533 /**
5534  * @class Roo.bootstrap.nav.Bar
5535  * @extends Roo.bootstrap.Component
5536  * @abstract
5537  * Bootstrap Navbar class
5538
5539  * @constructor
5540  * Create a new Navbar
5541  * @param {Object} config The config object
5542  */
5543
5544
5545 Roo.bootstrap.nav.Bar = function(config){
5546     Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5547     this.addEvents({
5548         // raw events
5549         /**
5550          * @event beforetoggle
5551          * Fire before toggle the menu
5552          * @param {Roo.EventObject} e
5553          */
5554         "beforetoggle" : true
5555     });
5556 };
5557
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component,  {
5559     
5560     
5561    
5562     // private
5563     navItems : false,
5564     loadMask : false,
5565     
5566     
5567     getAutoCreate : function(){
5568         
5569         
5570         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5571         
5572     },
5573     
5574     initEvents :function ()
5575     {
5576         //Roo.log(this.el.select('.navbar-toggle',true));
5577         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5578         
5579         var mark = {
5580             tag: "div",
5581             cls:"x-dlg-mask"
5582         };
5583         
5584         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5585         
5586         var size = this.el.getSize();
5587         this.maskEl.setSize(size.width, size.height);
5588         this.maskEl.enableDisplayMode("block");
5589         this.maskEl.hide();
5590         
5591         if(this.loadMask){
5592             this.maskEl.show();
5593         }
5594     },
5595     
5596     
5597     getChildContainer : function()
5598     {
5599         if (this.el && this.el.select('.collapse').getCount()) {
5600             return this.el.select('.collapse',true).first();
5601         }
5602         
5603         return this.el;
5604     },
5605     
5606     mask : function()
5607     {
5608         this.maskEl.show();
5609     },
5610     
5611     unmask : function()
5612     {
5613         this.maskEl.hide();
5614     },
5615     onToggle : function()
5616     {
5617         
5618         if(this.fireEvent('beforetoggle', this) === false){
5619             return;
5620         }
5621         var ce = this.el.select('.navbar-collapse',true).first();
5622       
5623         if (!ce.hasClass('show')) {
5624            this.expand();
5625         } else {
5626             this.collapse();
5627         }
5628         
5629         
5630     
5631     },
5632     /**
5633      * Expand the navbar pulldown 
5634      */
5635     expand : function ()
5636     {
5637        
5638         var ce = this.el.select('.navbar-collapse',true).first();
5639         if (ce.hasClass('collapsing')) {
5640             return;
5641         }
5642         ce.dom.style.height = '';
5643                // show it...
5644         ce.addClass('in'); // old...
5645         ce.removeClass('collapse');
5646         ce.addClass('show');
5647         var h = ce.getHeight();
5648         Roo.log(h);
5649         ce.removeClass('show');
5650         // at this point we should be able to see it..
5651         ce.addClass('collapsing');
5652         
5653         ce.setHeight(0); // resize it ...
5654         ce.on('transitionend', function() {
5655             //Roo.log('done transition');
5656             ce.removeClass('collapsing');
5657             ce.addClass('show');
5658             ce.removeClass('collapse');
5659
5660             ce.dom.style.height = '';
5661         }, this, { single: true} );
5662         ce.setHeight(h);
5663         ce.dom.scrollTop = 0;
5664     },
5665     /**
5666      * Collapse the navbar pulldown 
5667      */
5668     collapse : function()
5669     {
5670          var ce = this.el.select('.navbar-collapse',true).first();
5671        
5672         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673             // it's collapsed or collapsing..
5674             return;
5675         }
5676         ce.removeClass('in'); // old...
5677         ce.setHeight(ce.getHeight());
5678         ce.removeClass('show');
5679         ce.addClass('collapsing');
5680         
5681         ce.on('transitionend', function() {
5682             ce.dom.style.height = '';
5683             ce.removeClass('collapsing');
5684             ce.addClass('collapse');
5685         }, this, { single: true} );
5686         ce.setHeight(0);
5687     }
5688     
5689     
5690     
5691 });
5692
5693
5694
5695  
5696
5697  /*
5698  * - LGPL
5699  *
5700  * navbar
5701  * 
5702  */
5703
5704 /**
5705  * @class Roo.bootstrap.nav.Simplebar
5706  * @extends Roo.bootstrap.nav.Bar
5707  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708  * Bootstrap Sidebar class
5709  *
5710  * @cfg {Boolean} inverse is inverted color
5711  * 
5712  * @cfg {String} type (nav | pills | tabs)
5713  * @cfg {Boolean} arrangement stacked | justified
5714  * @cfg {String} align (left | right) alignment
5715  * 
5716  * @cfg {Boolean} main (true|false) main nav bar? default false
5717  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5718  * 
5719  * @cfg {String} tag (header|footer|nav|div) default is nav 
5720
5721  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5722  * 
5723  * 
5724  * @constructor
5725  * Create a new Sidebar
5726  * @param {Object} config The config object
5727  */
5728
5729
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731     Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5732 };
5733
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar,  {
5735     
5736     inverse: false,
5737     
5738     type: false,
5739     arrangement: '',
5740     align : false,
5741     
5742     weight : 'light',
5743     
5744     main : false,
5745     
5746     
5747     tag : false,
5748     
5749     
5750     getAutoCreate : function(){
5751         
5752         
5753         var cfg = {
5754             tag : this.tag || 'div',
5755             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5756         };
5757         if (['light','white'].indexOf(this.weight) > -1) {
5758             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5759         }
5760         cfg.cls += ' bg-' + this.weight;
5761         
5762         if (this.inverse) {
5763             cfg.cls += ' navbar-inverse';
5764             
5765         }
5766         
5767         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5768         
5769         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5770             return cfg;
5771         }
5772         
5773         
5774     
5775         
5776         cfg.cn = [
5777             {
5778                 cls: 'nav nav-' + this.xtype,
5779                 tag : 'ul'
5780             }
5781         ];
5782         
5783          
5784         this.type = this.type || 'nav';
5785         if (['tabs','pills'].indexOf(this.type) != -1) {
5786             cfg.cn[0].cls += ' nav-' + this.type
5787         
5788         
5789         } else {
5790             if (this.type!=='nav') {
5791                 Roo.log('nav type must be nav/tabs/pills')
5792             }
5793             cfg.cn[0].cls += ' navbar-nav'
5794         }
5795         
5796         
5797         
5798         
5799         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800             cfg.cn[0].cls += ' nav-' + this.arrangement;
5801         }
5802         
5803         
5804         if (this.align === 'right') {
5805             cfg.cn[0].cls += ' navbar-right';
5806         }
5807         
5808         
5809         
5810         
5811         return cfg;
5812     
5813         
5814     }
5815     
5816     
5817     
5818 });
5819
5820
5821
5822  
5823
5824  
5825        /*
5826  * - LGPL
5827  *
5828  * navbar
5829  * navbar-fixed-top
5830  * navbar-expand-md  fixed-top 
5831  */
5832
5833 /**
5834  * @class Roo.bootstrap.nav.Headerbar
5835  * @extends Roo.bootstrap.nav.Simplebar
5836  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837  * Bootstrap Sidebar class
5838  *
5839  * @cfg {String} brand what is brand
5840  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841  * @cfg {String} brand_href href of the brand
5842  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5843  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5846  * 
5847  * @constructor
5848  * Create a new Sidebar
5849  * @param {Object} config The config object
5850  */
5851
5852
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854     Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5855       
5856 };
5857
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar,  {
5859     
5860     position: '',
5861     brand: '',
5862     brand_href: false,
5863     srButton : true,
5864     autohide : false,
5865     desktopCenter : false,
5866    
5867     
5868     getAutoCreate : function(){
5869         
5870         var   cfg = {
5871             tag: this.nav || 'nav',
5872             cls: 'navbar navbar-expand-md',
5873             role: 'navigation',
5874             cn: []
5875         };
5876         
5877         var cn = cfg.cn;
5878         if (this.desktopCenter) {
5879             cn.push({cls : 'container', cn : []});
5880             cn = cn[0].cn;
5881         }
5882         
5883         if(this.srButton){
5884             var btn = {
5885                 tag: 'button',
5886                 type: 'button',
5887                 cls: 'navbar-toggle navbar-toggler',
5888                 'data-toggle': 'collapse',
5889                 cn: [
5890                     {
5891                         tag: 'span',
5892                         cls: 'sr-only',
5893                         html: 'Toggle navigation'
5894                     },
5895                     {
5896                         tag: 'span',
5897                         cls: 'icon-bar navbar-toggler-icon'
5898                     },
5899                     {
5900                         tag: 'span',
5901                         cls: 'icon-bar'
5902                     },
5903                     {
5904                         tag: 'span',
5905                         cls: 'icon-bar'
5906                     }
5907                 ]
5908             };
5909             
5910             cn.push( Roo.bootstrap.version == 4 ? btn : {
5911                 tag: 'div',
5912                 cls: 'navbar-header',
5913                 cn: [
5914                     btn
5915                 ]
5916             });
5917         }
5918         
5919         cn.push({
5920             tag: 'div',
5921             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5922             cn : []
5923         });
5924         
5925         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5926         
5927         if (['light','white'].indexOf(this.weight) > -1) {
5928             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5929         }
5930         cfg.cls += ' bg-' + this.weight;
5931         
5932         
5933         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5935             
5936             // tag can override this..
5937             
5938             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5939         }
5940         
5941         if (this.brand !== '') {
5942             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5944                 tag: 'a',
5945                 href: this.brand_href ? this.brand_href : '#',
5946                 cls: 'navbar-brand',
5947                 cn: [
5948                 this.brand
5949                 ]
5950             });
5951         }
5952         
5953         if(this.main){
5954             cfg.cls += ' main-nav';
5955         }
5956         
5957         
5958         return cfg;
5959
5960         
5961     },
5962     getHeaderChildContainer : function()
5963     {
5964         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965             return this.el.select('.navbar-header',true).first();
5966         }
5967         
5968         return this.getChildContainer();
5969     },
5970     
5971     getChildContainer : function()
5972     {
5973          
5974         return this.el.select('.roo-navbar-collapse',true).first();
5975          
5976         
5977     },
5978     
5979     initEvents : function()
5980     {
5981         Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5982         
5983         if (this.autohide) {
5984             
5985             var prevScroll = 0;
5986             var ft = this.el;
5987             
5988             Roo.get(document).on('scroll',function(e) {
5989                 var ns = Roo.get(document).getScroll().top;
5990                 var os = prevScroll;
5991                 prevScroll = ns;
5992                 
5993                 if(ns > os){
5994                     ft.removeClass('slideDown');
5995                     ft.addClass('slideUp');
5996                     return;
5997                 }
5998                 ft.removeClass('slideUp');
5999                 ft.addClass('slideDown');
6000                  
6001               
6002           },this);
6003         }
6004     }    
6005     
6006 });
6007
6008
6009
6010  
6011
6012  /*
6013  * - LGPL
6014  *
6015  * navbar
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.nav.Sidebar
6021  * @extends Roo.bootstrap.nav.Bar
6022  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023  * Bootstrap Sidebar class
6024  * 
6025  * @constructor
6026  * Create a new Sidebar
6027  * @param {Object} config The config object
6028  */
6029
6030
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032     Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6033 };
6034
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar,  {
6036     
6037     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6038     
6039     getAutoCreate : function(){
6040         
6041         
6042         return  {
6043             tag: 'div',
6044             cls: 'sidebar sidebar-nav'
6045         };
6046     
6047         
6048     }
6049     
6050     
6051     
6052 });
6053
6054
6055
6056  
6057
6058  /*
6059  * - LGPL
6060  *
6061  * nav group
6062  * 
6063  */
6064
6065 /**
6066  * @class Roo.bootstrap.nav.Group
6067  * @extends Roo.bootstrap.Component
6068  * @children Roo.bootstrap.nav.Item
6069  * Bootstrap NavGroup class
6070  * @cfg {String} align (left|right)
6071  * @cfg {Boolean} inverse
6072  * @cfg {String} type (nav|pills|tab) default nav
6073  * @cfg {String} navId - reference Id for navbar.
6074  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6075  * 
6076  * @constructor
6077  * Create a new nav group
6078  * @param {Object} config The config object
6079  */
6080
6081 Roo.bootstrap.nav.Group = function(config){
6082     Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6083     this.navItems = [];
6084    
6085     Roo.bootstrap.nav.Group.register(this);
6086      this.addEvents({
6087         /**
6088              * @event changed
6089              * Fires when the active item changes
6090              * @param {Roo.bootstrap.nav.Group} this
6091              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6093          */
6094         'changed': true
6095      });
6096     
6097 };
6098
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component,  {
6100     
6101     align: '',
6102     inverse: false,
6103     form: false,
6104     type: 'nav',
6105     navId : '',
6106     // private
6107     pilltype : true,
6108     
6109     navItems : false, 
6110     
6111     getAutoCreate : function()
6112     {
6113         var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6114         
6115         cfg = {
6116             tag : 'ul',
6117             cls: 'nav' 
6118         };
6119         if (Roo.bootstrap.version == 4) {
6120             if (['tabs','pills'].indexOf(this.type) != -1) {
6121                 cfg.cls += ' nav-' + this.type; 
6122             } else {
6123                 // trying to remove so header bar can right align top?
6124                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125                     // do not use on header bar... 
6126                     cfg.cls += ' navbar-nav';
6127                 }
6128             }
6129             
6130         } else {
6131             if (['tabs','pills'].indexOf(this.type) != -1) {
6132                 cfg.cls += ' nav-' + this.type
6133             } else {
6134                 if (this.type !== 'nav') {
6135                     Roo.log('nav type must be nav/tabs/pills')
6136                 }
6137                 cfg.cls += ' navbar-nav'
6138             }
6139         }
6140         
6141         if (this.parent() && this.parent().sidebar) {
6142             cfg = {
6143                 tag: 'ul',
6144                 cls: 'dashboard-menu sidebar-menu'
6145             };
6146             
6147             return cfg;
6148         }
6149         
6150         if (this.form === true) {
6151             cfg = {
6152                 tag: 'form',
6153                 cls: 'navbar-form form-inline'
6154             };
6155             //nav navbar-right ml-md-auto
6156             if (this.align === 'right') {
6157                 cfg.cls += ' navbar-right ml-md-auto';
6158             } else {
6159                 cfg.cls += ' navbar-left';
6160             }
6161         }
6162         
6163         if (this.align === 'right') {
6164             cfg.cls += ' navbar-right ml-md-auto';
6165         } else {
6166             cfg.cls += ' mr-auto';
6167         }
6168         
6169         if (this.inverse) {
6170             cfg.cls += ' navbar-inverse';
6171             
6172         }
6173         
6174         
6175         return cfg;
6176     },
6177     /**
6178     * sets the active Navigation item
6179     * @param {Roo.bootstrap.nav.Item} the new current navitem
6180     */
6181     setActiveItem : function(item)
6182     {
6183         var prev = false;
6184         Roo.each(this.navItems, function(v){
6185             if (v == item) {
6186                 return ;
6187             }
6188             if (v.isActive()) {
6189                 v.setActive(false, true);
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195
6196         item.setActive(true, true);
6197         this.fireEvent('changed', this, item, prev);
6198         
6199         
6200     },
6201     /**
6202     * gets the active Navigation item
6203     * @return {Roo.bootstrap.nav.Item} the current navitem
6204     */
6205     getActive : function()
6206     {
6207         
6208         var prev = false;
6209         Roo.each(this.navItems, function(v){
6210             
6211             if (v.isActive()) {
6212                 prev = v;
6213                 
6214             }
6215             
6216         });
6217         return prev;
6218     },
6219     
6220     indexOfNav : function()
6221     {
6222         
6223         var prev = false;
6224         Roo.each(this.navItems, function(v,i){
6225             
6226             if (v.isActive()) {
6227                 prev = i;
6228                 
6229             }
6230             
6231         });
6232         return prev;
6233     },
6234     /**
6235     * adds a Navigation item
6236     * @param {Roo.bootstrap.nav.Item} the navitem to add
6237     */
6238     addItem : function(cfg)
6239     {
6240         if (this.form && Roo.bootstrap.version == 4) {
6241             cfg.tag = 'div';
6242         }
6243         var cn = new Roo.bootstrap.nav.Item(cfg);
6244         this.register(cn);
6245         cn.parentId = this.id;
6246         cn.onRender(this.el, null);
6247         return cn;
6248     },
6249     /**
6250     * register a Navigation item
6251     * @param {Roo.bootstrap.nav.Item} the navitem to add
6252     */
6253     register : function(item)
6254     {
6255         this.navItems.push( item);
6256         item.navId = this.navId;
6257     
6258     },
6259     
6260     /**
6261     * clear all the Navigation item
6262     */
6263    
6264     clearAll : function()
6265     {
6266         this.navItems = [];
6267         this.el.dom.innerHTML = '';
6268     },
6269     
6270     getNavItem: function(tabId)
6271     {
6272         var ret = false;
6273         Roo.each(this.navItems, function(e) {
6274             if (e.tabId == tabId) {
6275                ret =  e;
6276                return false;
6277             }
6278             return true;
6279             
6280         });
6281         return ret;
6282     },
6283     
6284     setActiveNext : function()
6285     {
6286         var i = this.indexOfNav(this.getActive());
6287         if (i > this.navItems.length) {
6288             return;
6289         }
6290         this.setActiveItem(this.navItems[i+1]);
6291     },
6292     setActivePrev : function()
6293     {
6294         var i = this.indexOfNav(this.getActive());
6295         if (i  < 1) {
6296             return;
6297         }
6298         this.setActiveItem(this.navItems[i-1]);
6299     },
6300     clearWasActive : function(except) {
6301         Roo.each(this.navItems, function(e) {
6302             if (e.tabId != except.tabId && e.was_active) {
6303                e.was_active = false;
6304                return false;
6305             }
6306             return true;
6307             
6308         });
6309     },
6310     getWasActive : function ()
6311     {
6312         var r = false;
6313         Roo.each(this.navItems, function(e) {
6314             if (e.was_active) {
6315                r = e;
6316                return false;
6317             }
6318             return true;
6319             
6320         });
6321         return r;
6322     }
6323     
6324     
6325 });
6326
6327  
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6329     
6330     groups: {},
6331      /**
6332     * register a Navigation Group
6333     * @param {Roo.bootstrap.nav.Group} the navgroup to add
6334     */
6335     register : function(navgrp)
6336     {
6337         this.groups[navgrp.navId] = navgrp;
6338         
6339     },
6340     /**
6341     * fetch a Navigation Group based on the navigation ID
6342     * @param {string} the navgroup to add
6343     * @returns {Roo.bootstrap.nav.Group} the navgroup 
6344     */
6345     get: function(navId) {
6346         if (typeof(this.groups[navId]) == 'undefined') {
6347             return false;
6348             //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6349         }
6350         return this.groups[navId] ;
6351     }
6352     
6353     
6354     
6355 });
6356
6357  /**
6358  * @class Roo.bootstrap.nav.Item
6359  * @extends Roo.bootstrap.Component
6360  * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361  * @parent Roo.bootstrap.nav.Group
6362  * @licence LGPL
6363  * Bootstrap Navbar.NavItem class
6364  * 
6365  * @cfg {String} href  link to
6366  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367  * @cfg {Boolean} button_outline show and outlined button
6368  * @cfg {String} html content of button
6369  * @cfg {String} badge text inside badge
6370  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371  * @cfg {String} glyphicon DEPRICATED - use fa
6372  * @cfg {String} icon DEPRICATED - use fa
6373  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374  * @cfg {Boolean} active Is item active
6375  * @cfg {Boolean} disabled Is item disabled
6376  * @cfg {String} linkcls  Link Class
6377  * @cfg {Boolean} preventDefault (true | false) default false
6378  * @cfg {String} tabId the tab that this item activates.
6379  * @cfg {String} tagtype (a|span) render as a href or span?
6380  * @cfg {Boolean} animateRef (true|false) link to element default false  
6381  * @cfg {Roo.bootstrap.menu.Menu} menu a Menu 
6382   
6383  * @constructor
6384  * Create a new Navbar Item
6385  * @param {Object} config The config object
6386  */
6387 Roo.bootstrap.nav.Item = function(config){
6388     Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6389     this.addEvents({
6390         // raw events
6391         /**
6392          * @event click
6393          * The raw click event for the entire grid.
6394          * @param {Roo.EventObject} e
6395          */
6396         "click" : true,
6397          /**
6398             * @event changed
6399             * Fires when the active item active state changes
6400             * @param {Roo.bootstrap.nav.Item} this
6401             * @param {boolean} state the new state
6402              
6403          */
6404         'changed': true,
6405         /**
6406             * @event scrollto
6407             * Fires when scroll to element
6408             * @param {Roo.bootstrap.nav.Item} this
6409             * @param {Object} options
6410             * @param {Roo.EventObject} e
6411              
6412          */
6413         'scrollto': true
6414     });
6415    
6416 };
6417
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component,  {
6419     
6420     href: false,
6421     html: '',
6422     badge: '',
6423     icon: false,
6424     fa : false,
6425     glyphicon: false,
6426     active: false,
6427     preventDefault : false,
6428     tabId : false,
6429     tagtype : 'a',
6430     tag: 'li',
6431     disabled : false,
6432     animateRef : false,
6433     was_active : false,
6434     button_weight : '',
6435     button_outline : false,
6436     linkcls : '',
6437     navLink: false,
6438     
6439     getAutoCreate : function(){
6440          
6441         var cfg = {
6442             tag: this.tag,
6443             cls: 'nav-item'
6444         };
6445         
6446         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6447         
6448         if (this.active) {
6449             cfg.cls +=  ' active' ;
6450         }
6451         if (this.disabled) {
6452             cfg.cls += ' disabled';
6453         }
6454         
6455         // BS4 only?
6456         if (this.button_weight.length) {
6457             cfg.tag = this.href ? 'a' : 'button';
6458             cfg.html = this.html || '';
6459             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6460             if (this.href) {
6461                 cfg.href = this.href;
6462             }
6463             if (this.fa) {
6464                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6465             } else {
6466                 cfg.cls += " nav-html";
6467             }
6468             
6469             // menu .. should add dropdown-menu class - so no need for carat..
6470             
6471             if (this.badge !== '') {
6472                  
6473                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6474             }
6475             return cfg;
6476         }
6477         
6478         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6479             cfg.cn = [
6480                 {
6481                     tag: this.tagtype,
6482                     href : this.href || "#",
6483                     html: this.html || '',
6484                     cls : ''
6485                 }
6486             ];
6487             if (this.tagtype == 'a') {
6488                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6489         
6490             }
6491             if (this.icon) {
6492                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493             } else  if (this.fa) {
6494                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495             } else if(this.glyphicon) {
6496                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6497             } else {
6498                 cfg.cn[0].cls += " nav-html";
6499             }
6500             
6501             if (this.menu) {
6502                 cfg.cn[0].html += " <span class='caret'></span>";
6503              
6504             }
6505             
6506             if (this.badge !== '') {
6507                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6508             }
6509         }
6510         
6511         
6512         
6513         return cfg;
6514     },
6515     onRender : function(ct, position)
6516     {
6517        // Roo.log("Call onRender: " + this.xtype);
6518         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6519             this.tag = 'div';
6520         }
6521         
6522         var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523         this.navLink = this.el.select('.nav-link',true).first();
6524         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6525         return ret;
6526     },
6527       
6528     
6529     initEvents: function() 
6530     {
6531         if (typeof (this.menu) != 'undefined') {
6532             this.menu.parentType = this.xtype;
6533             this.menu.triggerEl = this.el;
6534             this.menu = this.addxtype(Roo.apply({}, this.menu));
6535         }
6536         
6537         this.el.on('click', this.onClick, this);
6538         
6539         //if(this.tagtype == 'span'){
6540         //    this.el.select('span',true).on('click', this.onClick, this);
6541         //}
6542        
6543         // at this point parent should be available..
6544         this.parent().register(this);
6545     },
6546     
6547     onClick : function(e)
6548     {
6549         if (e.getTarget('.dropdown-menu-item')) {
6550             // did you click on a menu itemm.... - then don't trigger onclick..
6551             return;
6552         }
6553         
6554         if(
6555                 this.preventDefault ||
6556                                 this.href === false ||
6557                 this.href === '#' 
6558         ){
6559             //Roo.log("NavItem - prevent Default?");
6560             e.preventDefault();
6561         }
6562         
6563         if (this.disabled) {
6564             return;
6565         }
6566         
6567         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568         if (tg && tg.transition) {
6569             Roo.log("waiting for the transitionend");
6570             return;
6571         }
6572         
6573         
6574         
6575         //Roo.log("fire event clicked");
6576         if(this.fireEvent('click', this, e) === false){
6577             return;
6578         };
6579         
6580         if(this.tagtype == 'span'){
6581             return;
6582         }
6583         
6584         //Roo.log(this.href);
6585         var ael = this.el.select('a',true).first();
6586         //Roo.log(ael);
6587         
6588         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591                 return; // ignore... - it's a 'hash' to another page.
6592             }
6593             Roo.log("NavItem - prevent Default?");
6594             e.preventDefault();
6595             this.scrollToElement(e);
6596         }
6597         
6598         
6599         var p =  this.parent();
6600    
6601         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602             if (typeof(p.setActiveItem) !== 'undefined') {
6603                 p.setActiveItem(this);
6604             }
6605         }
6606         
6607         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609             // remove the collapsed menu expand...
6610             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6611         }
6612     },
6613     
6614     isActive: function () {
6615         return this.active
6616     },
6617     setActive : function(state, fire, is_was_active)
6618     {
6619         if (this.active && !state && this.navId) {
6620             this.was_active = true;
6621             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6622             if (nv) {
6623                 nv.clearWasActive(this);
6624             }
6625             
6626         }
6627         this.active = state;
6628         
6629         if (!state ) {
6630             this.el.removeClass('active');
6631             this.navLink ? this.navLink.removeClass('active') : false;
6632         } else if (!this.el.hasClass('active')) {
6633             
6634             this.el.addClass('active');
6635             if (Roo.bootstrap.version == 4 && this.navLink ) {
6636                 this.navLink.addClass('active');
6637             }
6638             
6639         }
6640         if (fire) {
6641             this.fireEvent('changed', this, state);
6642         }
6643         
6644         // show a panel if it's registered and related..
6645         
6646         if (!this.navId || !this.tabId || !state || is_was_active) {
6647             return;
6648         }
6649         
6650         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6651         if (!tg) {
6652             return;
6653         }
6654         var pan = tg.getPanelByName(this.tabId);
6655         if (!pan) {
6656             return;
6657         }
6658         // if we can not flip to new panel - go back to old nav highlight..
6659         if (false == tg.showPanel(pan)) {
6660             var nv = Roo.bootstrap.nav.Group.get(this.navId);
6661             if (nv) {
6662                 var onav = nv.getWasActive();
6663                 if (onav) {
6664                     onav.setActive(true, false, true);
6665                 }
6666             }
6667             
6668         }
6669         
6670         
6671         
6672     },
6673      // this should not be here...
6674     setDisabled : function(state)
6675     {
6676         this.disabled = state;
6677         if (!state ) {
6678             this.el.removeClass('disabled');
6679         } else if (!this.el.hasClass('disabled')) {
6680             this.el.addClass('disabled');
6681         }
6682         
6683     },
6684     
6685     /**
6686      * Fetch the element to display the tooltip on.
6687      * @return {Roo.Element} defaults to this.el
6688      */
6689     tooltipEl : function()
6690     {
6691         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6692     },
6693     
6694     scrollToElement : function(e)
6695     {
6696         var c = document.body;
6697         
6698         /*
6699          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6700          */
6701         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702             c = document.documentElement;
6703         }
6704         
6705         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6706         
6707         if(!target){
6708             return;
6709         }
6710
6711         var o = target.calcOffsetsTo(c);
6712         
6713         var options = {
6714             target : target,
6715             value : o[1]
6716         };
6717         
6718         this.fireEvent('scrollto', this, options, e);
6719         
6720         Roo.get(c).scrollTo('top', options.value, true);
6721         
6722         return;
6723     },
6724     /**
6725      * Set the HTML (text content) of the item
6726      * @param {string} html  content for the nav item
6727      */
6728     setHtml : function(html)
6729     {
6730         this.html = html;
6731         this.htmlEl.dom.innerHTML = html;
6732         
6733     } 
6734 });
6735  
6736
6737  /*
6738  * - LGPL
6739  *
6740  * sidebar item
6741  *
6742  *  li
6743  *    <span> icon </span>
6744  *    <span> text </span>
6745  *    <span>badge </span>
6746  */
6747
6748 /**
6749  * @class Roo.bootstrap.nav.SidebarItem
6750  * @extends Roo.bootstrap.nav.Item
6751  * Bootstrap Navbar.NavSidebarItem class
6752  * 
6753  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754  * {Boolean} open is the menu open
6755  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757  * {String} buttonSize (sm|md|lg)the extra classes for the button
6758  * {Boolean} showArrow show arrow next to the text (default true)
6759  * @constructor
6760  * Create a new Navbar Button
6761  * @param {Object} config The config object
6762  */
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764     Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6765     this.addEvents({
6766         // raw events
6767         /**
6768          * @event click
6769          * The raw click event for the entire grid.
6770          * @param {Roo.EventObject} e
6771          */
6772         "click" : true,
6773          /**
6774             * @event changed
6775             * Fires when the active item active state changes
6776             * @param {Roo.bootstrap.nav.SidebarItem} this
6777             * @param {boolean} state the new state
6778              
6779          */
6780         'changed': true
6781     });
6782    
6783 };
6784
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item,  {
6786     
6787     badgeWeight : 'default',
6788     
6789     open: false,
6790     
6791     buttonView : false,
6792     
6793     buttonWeight : 'default',
6794     
6795     buttonSize : 'md',
6796     
6797     showArrow : true,
6798     
6799     getAutoCreate : function(){
6800         
6801         
6802         var a = {
6803                 tag: 'a',
6804                 href : this.href || '#',
6805                 cls: '',
6806                 html : '',
6807                 cn : []
6808         };
6809         
6810         if(this.buttonView){
6811             a = {
6812                 tag: 'button',
6813                 href : this.href || '#',
6814                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6815                 html : this.html,
6816                 cn : []
6817             };
6818         }
6819         
6820         var cfg = {
6821             tag: 'li',
6822             cls: '',
6823             cn: [ a ]
6824         };
6825         
6826         if (this.active) {
6827             cfg.cls += ' active';
6828         }
6829         
6830         if (this.disabled) {
6831             cfg.cls += ' disabled';
6832         }
6833         if (this.open) {
6834             cfg.cls += ' open x-open';
6835         }
6836         // left icon..
6837         if (this.glyphicon || this.icon) {
6838             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6839             a.cn.push({ tag : 'i', cls : c }) ;
6840         }
6841         
6842         if(!this.buttonView){
6843             var span = {
6844                 tag: 'span',
6845                 html : this.html || ''
6846             };
6847
6848             a.cn.push(span);
6849             
6850         }
6851         
6852         if (this.badge !== '') {
6853             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6854         }
6855         
6856         if (this.menu) {
6857             
6858             if(this.showArrow){
6859                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6860             }
6861             
6862             a.cls += ' dropdown-toggle treeview' ;
6863         }
6864         
6865         return cfg;
6866     },
6867     
6868     initEvents : function()
6869     { 
6870         if (typeof (this.menu) != 'undefined') {
6871             this.menu.parentType = this.xtype;
6872             this.menu.triggerEl = this.el;
6873             this.menu = this.addxtype(Roo.apply({}, this.menu));
6874         }
6875         
6876         this.el.on('click', this.onClick, this);
6877         
6878         if(this.badge !== ''){
6879             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6880         }
6881         
6882     },
6883     
6884     onClick : function(e)
6885     {
6886         if(this.disabled){
6887             e.preventDefault();
6888             return;
6889         }
6890         
6891         if(this.preventDefault){
6892             e.preventDefault();
6893         }
6894         
6895         this.fireEvent('click', this, e);
6896     },
6897     
6898     disable : function()
6899     {
6900         this.setDisabled(true);
6901     },
6902     
6903     enable : function()
6904     {
6905         this.setDisabled(false);
6906     },
6907     
6908     setDisabled : function(state)
6909     {
6910         if(this.disabled == state){
6911             return;
6912         }
6913         
6914         this.disabled = state;
6915         
6916         if (state) {
6917             this.el.addClass('disabled');
6918             return;
6919         }
6920         
6921         this.el.removeClass('disabled');
6922         
6923         return;
6924     },
6925     
6926     setActive : function(state)
6927     {
6928         if(this.active == state){
6929             return;
6930         }
6931         
6932         this.active = state;
6933         
6934         if (state) {
6935             this.el.addClass('active');
6936             return;
6937         }
6938         
6939         this.el.removeClass('active');
6940         
6941         return;
6942     },
6943     
6944     isActive: function () 
6945     {
6946         return this.active;
6947     },
6948     
6949     setBadge : function(str)
6950     {
6951         if(!this.badgeEl){
6952             return;
6953         }
6954         
6955         this.badgeEl.dom.innerHTML = str;
6956     }
6957     
6958    
6959      
6960  
6961 });
6962  
6963
6964  /*
6965  * - LGPL
6966  *
6967  * nav progress bar
6968  * 
6969  */
6970
6971 /**
6972  * @class Roo.bootstrap.nav.ProgressBar
6973  * @extends Roo.bootstrap.Component
6974  * @children Roo.bootstrap.nav.ProgressBarItem
6975  * Bootstrap NavProgressBar class
6976  * 
6977  * @constructor
6978  * Create a new nav progress bar - a bar indicating step along a process
6979  * @param {Object} config The config object
6980  */
6981
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983     Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6984
6985     this.bullets = this.bullets || [];
6986    
6987 //    Roo.bootstrap.nav.ProgressBar.register(this);
6988      this.addEvents({
6989         /**
6990              * @event changed
6991              * Fires when the active item changes
6992              * @param {Roo.bootstrap.nav.ProgressBar} this
6993              * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994              * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item 
6995          */
6996         'changed': true
6997      });
6998     
6999 };
7000
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component,  {
7002     /**
7003      * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004      * Bullets for the Nav Progress bar for the toolbar
7005      */
7006     bullets : [],
7007     barItems : [],
7008     
7009     getAutoCreate : function()
7010     {
7011         var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7012         
7013         cfg = {
7014             tag : 'div',
7015             cls : 'roo-navigation-bar-group',
7016             cn : [
7017                 {
7018                     tag : 'div',
7019                     cls : 'roo-navigation-top-bar'
7020                 },
7021                 {
7022                     tag : 'div',
7023                     cls : 'roo-navigation-bullets-bar',
7024                     cn : [
7025                         {
7026                             tag : 'ul',
7027                             cls : 'roo-navigation-bar'
7028                         }
7029                     ]
7030                 },
7031                 
7032                 {
7033                     tag : 'div',
7034                     cls : 'roo-navigation-bottom-bar'
7035                 }
7036             ]
7037             
7038         };
7039         
7040         return cfg;
7041         
7042     },
7043     
7044     initEvents: function() 
7045     {
7046         
7047     },
7048     
7049     onRender : function(ct, position) 
7050     {
7051         Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7052         
7053         if(this.bullets.length){
7054             Roo.each(this.bullets, function(b){
7055                this.addItem(b);
7056             }, this);
7057         }
7058         
7059         this.format();
7060         
7061     },
7062     
7063     addItem : function(cfg)
7064     {
7065         var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7066         
7067         item.parentId = this.id;
7068         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7069         
7070         if(cfg.html){
7071             var top = new Roo.bootstrap.Element({
7072                 tag : 'div',
7073                 cls : 'roo-navigation-bar-text'
7074             });
7075             
7076             var bottom = new Roo.bootstrap.Element({
7077                 tag : 'div',
7078                 cls : 'roo-navigation-bar-text'
7079             });
7080             
7081             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7083             
7084             var topText = new Roo.bootstrap.Element({
7085                 tag : 'span',
7086                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7087             });
7088             
7089             var bottomText = new Roo.bootstrap.Element({
7090                 tag : 'span',
7091                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7092             });
7093             
7094             topText.onRender(top.el, null);
7095             bottomText.onRender(bottom.el, null);
7096             
7097             item.topEl = top;
7098             item.bottomEl = bottom;
7099         }
7100         
7101         this.barItems.push(item);
7102         
7103         return item;
7104     },
7105     
7106     getActive : function()
7107     {
7108         var active = false;
7109         
7110         Roo.each(this.barItems, function(v){
7111             
7112             if (!v.isActive()) {
7113                 return;
7114             }
7115             
7116             active = v;
7117             return false;
7118             
7119         });
7120         
7121         return active;
7122     },
7123     
7124     setActiveItem : function(item)
7125     {
7126         var prev = false;
7127         
7128         Roo.each(this.barItems, function(v){
7129             if (v.rid == item.rid) {
7130                 return ;
7131             }
7132             
7133             if (v.isActive()) {
7134                 v.setActive(false);
7135                 prev = v;
7136             }
7137         });
7138
7139         item.setActive(true);
7140         
7141         this.fireEvent('changed', this, item, prev);
7142     },
7143     
7144     getBarItem: function(rid)
7145     {
7146         var ret = false;
7147         
7148         Roo.each(this.barItems, function(e) {
7149             if (e.rid != rid) {
7150                 return;
7151             }
7152             
7153             ret =  e;
7154             return false;
7155         });
7156         
7157         return ret;
7158     },
7159     
7160     indexOfItem : function(item)
7161     {
7162         var index = false;
7163         
7164         Roo.each(this.barItems, function(v, i){
7165             
7166             if (v.rid != item.rid) {
7167                 return;
7168             }
7169             
7170             index = i;
7171             return false
7172         });
7173         
7174         return index;
7175     },
7176     
7177     setActiveNext : function()
7178     {
7179         var i = this.indexOfItem(this.getActive());
7180         
7181         if (i > this.barItems.length) {
7182             return;
7183         }
7184         
7185         this.setActiveItem(this.barItems[i+1]);
7186     },
7187     
7188     setActivePrev : function()
7189     {
7190         var i = this.indexOfItem(this.getActive());
7191         
7192         if (i  < 1) {
7193             return;
7194         }
7195         
7196         this.setActiveItem(this.barItems[i-1]);
7197     },
7198     
7199     format : function()
7200     {
7201         if(!this.barItems.length){
7202             return;
7203         }
7204      
7205         var width = 100 / this.barItems.length;
7206         
7207         Roo.each(this.barItems, function(i){
7208             i.el.setStyle('width', width + '%');
7209             i.topEl.el.setStyle('width', width + '%');
7210             i.bottomEl.el.setStyle('width', width + '%');
7211         }, this);
7212         
7213     }
7214     
7215 });
7216 /*
7217  * - LGPL
7218  *
7219  * Nav Progress Item
7220  * 
7221  */
7222
7223 /**
7224  * @class Roo.bootstrap.nav.ProgressBarItem
7225  * @extends Roo.bootstrap.Component
7226  * Bootstrap NavProgressBarItem class
7227  * @cfg {String} rid the reference id
7228  * @cfg {Boolean} active (true|false) Is item active default false
7229  * @cfg {Boolean} disabled (true|false) Is item active default false
7230  * @cfg {String} html
7231  * @cfg {String} position (top|bottom) text position default bottom
7232  * @cfg {String} icon show icon instead of number
7233  * 
7234  * @constructor
7235  * Create a new NavProgressBarItem
7236  * @param {Object} config The config object
7237  */
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239     Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7240     this.addEvents({
7241         // raw events
7242         /**
7243          * @event click
7244          * The raw click event for the entire grid.
7245          * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246          * @param {Roo.EventObject} e
7247          */
7248         "click" : true
7249     });
7250    
7251 };
7252
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component,  {
7254     
7255     rid : '',
7256     active : false,
7257     disabled : false,
7258     html : '',
7259     position : 'bottom',
7260     icon : false,
7261     
7262     getAutoCreate : function()
7263     {
7264         var iconCls = 'roo-navigation-bar-item-icon';
7265         
7266         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7267         
7268         var cfg = {
7269             tag: 'li',
7270             cls: 'roo-navigation-bar-item',
7271             cn : [
7272                 {
7273                     tag : 'i',
7274                     cls : iconCls
7275                 }
7276             ]
7277         };
7278         
7279         if(this.active){
7280             cfg.cls += ' active';
7281         }
7282         if(this.disabled){
7283             cfg.cls += ' disabled';
7284         }
7285         
7286         return cfg;
7287     },
7288     
7289     disable : function()
7290     {
7291         this.setDisabled(true);
7292     },
7293     
7294     enable : function()
7295     {
7296         this.setDisabled(false);
7297     },
7298     
7299     initEvents: function() 
7300     {
7301         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7302         
7303         this.iconEl.on('click', this.onClick, this);
7304     },
7305     
7306     onClick : function(e)
7307     {
7308         e.preventDefault();
7309         
7310         if(this.disabled){
7311             return;
7312         }
7313         
7314         if(this.fireEvent('click', this, e) === false){
7315             return;
7316         };
7317         
7318         this.parent().setActiveItem(this);
7319     },
7320     
7321     isActive: function () 
7322     {
7323         return this.active;
7324     },
7325     
7326     setActive : function(state)
7327     {
7328         if(this.active == state){
7329             return;
7330         }
7331         
7332         this.active = state;
7333         
7334         if (state) {
7335             this.el.addClass('active');
7336             return;
7337         }
7338         
7339         this.el.removeClass('active');
7340         
7341         return;
7342     },
7343     
7344     setDisabled : function(state)
7345     {
7346         if(this.disabled == state){
7347             return;
7348         }
7349         
7350         this.disabled = state;
7351         
7352         if (state) {
7353             this.el.addClass('disabled');
7354             return;
7355         }
7356         
7357         this.el.removeClass('disabled');
7358     },
7359     
7360     tooltipEl : function()
7361     {
7362         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7363     }
7364 });
7365  
7366
7367  /*
7368  * - LGPL
7369  *
7370  *  Breadcrumb Nav
7371  * 
7372  */
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7374
7375
7376 /**
7377  * @class Roo.bootstrap.breadcrumb.Nav
7378  * @extends Roo.bootstrap.Component
7379  * Bootstrap Breadcrumb Nav Class
7380  *  
7381  * @children Roo.bootstrap.breadcrumb.Item
7382  * 
7383  * @constructor
7384  * Create a new breadcrumb.Nav
7385  * @param {Object} config The config object
7386  */
7387
7388
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7391     
7392     
7393 };
7394
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
7396     
7397     getAutoCreate : function()
7398     {
7399
7400         var cfg = {
7401             tag: 'nav',
7402             cn : [
7403                 {
7404                     tag : 'ol',
7405                     cls : 'breadcrumb'
7406                 }
7407             ]
7408             
7409         };
7410           
7411         return cfg;
7412     },
7413     
7414     initEvents: function()
7415     {
7416         this.olEl = this.el.select('ol',true).first();    
7417     },
7418     getChildContainer : function()
7419     {
7420         return this.olEl;  
7421     }
7422     
7423 });
7424
7425  /*
7426  * - LGPL
7427  *
7428  *  Breadcrumb Item
7429  * 
7430  */
7431
7432
7433 /**
7434  * @class Roo.bootstrap.breadcrumb.Nav
7435  * @extends Roo.bootstrap.Component
7436  * @children Roo.bootstrap.Component
7437  * @parent Roo.bootstrap.breadcrumb.Nav
7438  * Bootstrap Breadcrumb Nav Class
7439  *  
7440  * 
7441  * @cfg {String} html the content of the link.
7442  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443  * @cfg {Boolean} active is it active
7444
7445  * 
7446  * @constructor
7447  * Create a new breadcrumb.Nav
7448  * @param {Object} config The config object
7449  */
7450
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7453     this.addEvents({
7454         // img events
7455         /**
7456          * @event click
7457          * The img click event for the img.
7458          * @param {Roo.EventObject} e
7459          */
7460         "click" : true
7461     });
7462     
7463 };
7464
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7466     
7467     href: false,
7468     html : '',
7469     
7470     getAutoCreate : function()
7471     {
7472
7473         var cfg = {
7474             tag: 'li',
7475             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7476         };
7477         if (this.href !== false) {
7478             cfg.cn = [{
7479                 tag : 'a',
7480                 href : this.href,
7481                 html : this.html
7482             }];
7483         } else {
7484             cfg.html = this.html;
7485         }
7486         
7487         return cfg;
7488     },
7489     
7490     initEvents: function()
7491     {
7492         if (this.href) {
7493             this.el.select('a', true).first().on('click',this.onClick, this)
7494         }
7495         
7496     },
7497     onClick : function(e)
7498     {
7499         e.preventDefault();
7500         this.fireEvent('click',this,  e);
7501     }
7502     
7503 });
7504
7505  /*
7506  * - LGPL
7507  *
7508  * row
7509  * 
7510  */
7511
7512 /**
7513  * @class Roo.bootstrap.Row
7514  * @extends Roo.bootstrap.Component
7515  * @children Roo.bootstrap.Component
7516  * Bootstrap Row class (contains columns...)
7517  * 
7518  * @constructor
7519  * Create a new Row
7520  * @param {Object} config The config object
7521  */
7522
7523 Roo.bootstrap.Row = function(config){
7524     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7525 };
7526
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7528     
7529     getAutoCreate : function(){
7530        return {
7531             cls: 'row clearfix'
7532        };
7533     }
7534     
7535     
7536 });
7537
7538  
7539
7540  /*
7541  * - LGPL
7542  *
7543  * pagination
7544  * 
7545  */
7546
7547 /**
7548  * @class Roo.bootstrap.Pagination
7549  * @extends Roo.bootstrap.Component
7550  * @children Roo.bootstrap.Pagination
7551  * Bootstrap Pagination class
7552  * 
7553  * @cfg {String} size (xs|sm|md|lg|xl)
7554  * @cfg {Boolean} inverse 
7555  * 
7556  * @constructor
7557  * Create a new Pagination
7558  * @param {Object} config The config object
7559  */
7560
7561 Roo.bootstrap.Pagination = function(config){
7562     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7563 };
7564
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7566     
7567     cls: false,
7568     size: false,
7569     inverse: false,
7570     
7571     getAutoCreate : function(){
7572         var cfg = {
7573             tag: 'ul',
7574                 cls: 'pagination'
7575         };
7576         if (this.inverse) {
7577             cfg.cls += ' inverse';
7578         }
7579         if (this.html) {
7580             cfg.html=this.html;
7581         }
7582         if (this.cls) {
7583             cfg.cls += " " + this.cls;
7584         }
7585         return cfg;
7586     }
7587    
7588 });
7589
7590  
7591
7592  /*
7593  * - LGPL
7594  *
7595  * Pagination item
7596  * 
7597  */
7598
7599
7600 /**
7601  * @class Roo.bootstrap.PaginationItem
7602  * @extends Roo.bootstrap.Component
7603  * Bootstrap PaginationItem class
7604  * @cfg {String} html text
7605  * @cfg {String} href the link
7606  * @cfg {Boolean} preventDefault (true | false) default true
7607  * @cfg {Boolean} active (true | false) default false
7608  * @cfg {Boolean} disabled default false
7609  * 
7610  * 
7611  * @constructor
7612  * Create a new PaginationItem
7613  * @param {Object} config The config object
7614  */
7615
7616
7617 Roo.bootstrap.PaginationItem = function(config){
7618     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7619     this.addEvents({
7620         // raw events
7621         /**
7622          * @event click
7623          * The raw click event for the entire grid.
7624          * @param {Roo.EventObject} e
7625          */
7626         "click" : true
7627     });
7628 };
7629
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7631     
7632     href : false,
7633     html : false,
7634     preventDefault: true,
7635     active : false,
7636     cls : false,
7637     disabled: false,
7638     
7639     getAutoCreate : function(){
7640         var cfg= {
7641             tag: 'li',
7642             cn: [
7643                 {
7644                     tag : 'a',
7645                     href : this.href ? this.href : '#',
7646                     html : this.html ? this.html : ''
7647                 }
7648             ]
7649         };
7650         
7651         if(this.cls){
7652             cfg.cls = this.cls;
7653         }
7654         
7655         if(this.disabled){
7656             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7657         }
7658         
7659         if(this.active){
7660             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7661         }
7662         
7663         return cfg;
7664     },
7665     
7666     initEvents: function() {
7667         
7668         this.el.on('click', this.onClick, this);
7669         
7670     },
7671     onClick : function(e)
7672     {
7673         Roo.log('PaginationItem on click ');
7674         if(this.preventDefault){
7675             e.preventDefault();
7676         }
7677         
7678         if(this.disabled){
7679             return;
7680         }
7681         
7682         this.fireEvent('click', this, e);
7683     }
7684    
7685 });
7686
7687  
7688
7689  /*
7690  * - LGPL
7691  *
7692  * slider
7693  * 
7694  */
7695
7696
7697 /**
7698  * @class Roo.bootstrap.Slider
7699  * @extends Roo.bootstrap.Component
7700  * Bootstrap Slider class
7701  *    
7702  * @constructor
7703  * Create a new Slider
7704  * @param {Object} config The config object
7705  */
7706
7707 Roo.bootstrap.Slider = function(config){
7708     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7709 };
7710
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7712     
7713     getAutoCreate : function(){
7714         
7715         var cfg = {
7716             tag: 'div',
7717             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7718             cn: [
7719                 {
7720                     tag: 'a',
7721                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722                 }
7723             ]
7724         };
7725         
7726         return cfg;
7727     }
7728    
7729 });
7730
7731  /*
7732  * Based on:
7733  * Ext JS Library 1.1.1
7734  * Copyright(c) 2006-2007, Ext JS, LLC.
7735  *
7736  * Originally Released Under LGPL - original licence link has changed is not relivant.
7737  *
7738  * Fork - LGPL
7739  * <script type="text/javascript">
7740  */
7741  /**
7742  * @extends Roo.dd.DDProxy
7743  * @class Roo.grid.SplitDragZone
7744  * Support for Column Header resizing
7745  * @constructor
7746  * @param {Object} config
7747  */
7748 // private
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7751     this.grid = grid;
7752     this.view = grid.getView();
7753     this.proxy = this.view.resizeProxy;
7754     Roo.grid.SplitDragZone.superclass.constructor.call(
7755         this,
7756         hd, // ID
7757         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7758         {  // CONFIG
7759             dragElId : Roo.id(this.proxy.dom),
7760             resizeFrame:false
7761         }
7762     );
7763     
7764     this.setHandleElId(Roo.id(hd));
7765     if (hd2 !== false) {
7766         this.setOuterHandleElId(Roo.id(hd2));
7767     }
7768     
7769     this.scroll = false;
7770 };
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772     fly: Roo.Element.fly,
7773
7774     b4StartDrag : function(x, y){
7775         this.view.headersDisabled = true;
7776         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7778         );
7779         this.proxy.setHeight(h);
7780         
7781         // for old system colWidth really stored the actual width?
7782         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783         // which in reality did not work.. - it worked only for fixed sizes
7784         // for resizable we need to use actual sizes.
7785         var w = this.cm.getColumnWidth(this.cellIndex);
7786         if (!this.view.mainWrap) {
7787             // bootstrap.
7788             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7789         }
7790         
7791         
7792         
7793         // this was w-this.grid.minColumnWidth;
7794         // doesnt really make sense? - w = thie curren width or the rendered one?
7795         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796         this.resetConstraints();
7797         this.setXConstraint(minw, 1000);
7798         this.setYConstraint(0, 0);
7799         this.minX = x - minw;
7800         this.maxX = x + 1000;
7801         this.startPos = x;
7802         if (!this.view.mainWrap) { // this is Bootstrap code..
7803             this.getDragEl().style.display='block';
7804         }
7805         
7806         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7807     },
7808
7809
7810     handleMouseDown : function(e){
7811         ev = Roo.EventObject.setEvent(e);
7812         var t = this.fly(ev.getTarget());
7813         if(t.hasClass("x-grid-split")){
7814             this.cellIndex = this.view.getCellIndex(t.dom);
7815             this.split = t.dom;
7816             this.cm = this.grid.colModel;
7817             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7819             }
7820         }
7821     },
7822
7823     endDrag : function(e){
7824         this.view.headersDisabled = false;
7825         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826         var diff = endX - this.startPos;
7827         // 
7828         var w = this.cm.getColumnWidth(this.cellIndex);
7829         if (!this.view.mainWrap) {
7830             w = 0;
7831         }
7832         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7833     },
7834
7835     autoOffset : function(){
7836         this.setDelta(0,0);
7837     }
7838 });/*
7839  * Based on:
7840  * Ext JS Library 1.1.1
7841  * Copyright(c) 2006-2007, Ext JS, LLC.
7842  *
7843  * Originally Released Under LGPL - original licence link has changed is not relivant.
7844  *
7845  * Fork - LGPL
7846  * <script type="text/javascript">
7847  */
7848
7849 /**
7850  * @class Roo.grid.AbstractSelectionModel
7851  * @extends Roo.util.Observable
7852  * @abstract
7853  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7854  * implemented by descendant classes.  This class should not be directly instantiated.
7855  * @constructor
7856  */
7857 Roo.grid.AbstractSelectionModel = function(){
7858     this.locked = false;
7859     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7860 };
7861
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7863     /** @ignore Called by the grid automatically. Do not call directly. */
7864     init : function(grid){
7865         this.grid = grid;
7866         this.initEvents();
7867     },
7868
7869     /**
7870      * Locks the selections.
7871      */
7872     lock : function(){
7873         this.locked = true;
7874     },
7875
7876     /**
7877      * Unlocks the selections.
7878      */
7879     unlock : function(){
7880         this.locked = false;
7881     },
7882
7883     /**
7884      * Returns true if the selections are locked.
7885      * @return {Boolean}
7886      */
7887     isLocked : function(){
7888         return this.locked;
7889     }
7890 });/*
7891  * Based on:
7892  * Ext JS Library 1.1.1
7893  * Copyright(c) 2006-2007, Ext JS, LLC.
7894  *
7895  * Originally Released Under LGPL - original licence link has changed is not relivant.
7896  *
7897  * Fork - LGPL
7898  * <script type="text/javascript">
7899  */
7900 /**
7901  * @extends Roo.grid.AbstractSelectionModel
7902  * @class Roo.grid.RowSelectionModel
7903  * The default SelectionModel used by {@link Roo.grid.Grid}.
7904  * It supports multiple selections and keyboard selection/navigation. 
7905  * @constructor
7906  * @param {Object} config
7907  */
7908 Roo.grid.RowSelectionModel = function(config){
7909     Roo.apply(this, config);
7910     this.selections = new Roo.util.MixedCollection(false, function(o){
7911         return o.id;
7912     });
7913
7914     this.last = false;
7915     this.lastActive = false;
7916
7917     this.addEvents({
7918         /**
7919         * @event selectionchange
7920         * Fires when the selection changes
7921         * @param {SelectionModel} this
7922         */
7923        "selectionchange" : true,
7924        /**
7925         * @event afterselectionchange
7926         * Fires after the selection changes (eg. by key press or clicking)
7927         * @param {SelectionModel} this
7928         */
7929        "afterselectionchange" : true,
7930        /**
7931         * @event beforerowselect
7932         * Fires when a row is selected being selected, return false to cancel.
7933         * @param {SelectionModel} this
7934         * @param {Number} rowIndex The selected index
7935         * @param {Boolean} keepExisting False if other selections will be cleared
7936         */
7937        "beforerowselect" : true,
7938        /**
7939         * @event rowselect
7940         * Fires when a row is selected.
7941         * @param {SelectionModel} this
7942         * @param {Number} rowIndex The selected index
7943         * @param {Roo.data.Record} r The record
7944         */
7945        "rowselect" : true,
7946        /**
7947         * @event rowdeselect
7948         * Fires when a row is deselected.
7949         * @param {SelectionModel} this
7950         * @param {Number} rowIndex The selected index
7951         */
7952         "rowdeselect" : true
7953     });
7954     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955     this.locked = false;
7956 };
7957
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7959     /**
7960      * @cfg {Boolean} singleSelect
7961      * True to allow selection of only one row at a time (defaults to false)
7962      */
7963     singleSelect : false,
7964
7965     // private
7966     initEvents : function(){
7967
7968         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969             this.grid.on("mousedown", this.handleMouseDown, this);
7970         }else{ // allow click to work like normal
7971             this.grid.on("rowclick", this.handleDragableRowClick, this);
7972         }
7973         // bootstrap does not have a view..
7974         var view = this.grid.view ? this.grid.view : this.grid;
7975         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7976             "up" : function(e){
7977                 if(!e.shiftKey){
7978                     this.selectPrevious(e.shiftKey);
7979                 }else if(this.last !== false && this.lastActive !== false){
7980                     var last = this.last;
7981                     this.selectRange(this.last,  this.lastActive-1);
7982                     view.focusRow(this.lastActive);
7983                     if(last !== false){
7984                         this.last = last;
7985                     }
7986                 }else{
7987                     this.selectFirstRow();
7988                 }
7989                 this.fireEvent("afterselectionchange", this);
7990             },
7991             "down" : function(e){
7992                 if(!e.shiftKey){
7993                     this.selectNext(e.shiftKey);
7994                 }else if(this.last !== false && this.lastActive !== false){
7995                     var last = this.last;
7996                     this.selectRange(this.last,  this.lastActive+1);
7997                     view.focusRow(this.lastActive);
7998                     if(last !== false){
7999                         this.last = last;
8000                     }
8001                 }else{
8002                     this.selectFirstRow();
8003                 }
8004                 this.fireEvent("afterselectionchange", this);
8005             },
8006             scope: this
8007         });
8008
8009          
8010         view.on("refresh", this.onRefresh, this);
8011         view.on("rowupdated", this.onRowUpdated, this);
8012         view.on("rowremoved", this.onRemove, this);
8013     },
8014
8015     // private
8016     onRefresh : function(){
8017         var ds = this.grid.ds, i, v = this.grid.view;
8018         var s = this.selections;
8019         s.each(function(r){
8020             if((i = ds.indexOfId(r.id)) != -1){
8021                 v.onRowSelect(i);
8022                 s.add(ds.getAt(i)); // updating the selection relate data
8023             }else{
8024                 s.remove(r);
8025             }
8026         });
8027     },
8028
8029     // private
8030     onRemove : function(v, index, r){
8031         this.selections.remove(r);
8032     },
8033
8034     // private
8035     onRowUpdated : function(v, index, r){
8036         if(this.isSelected(r)){
8037             v.onRowSelect(index);
8038         }
8039     },
8040
8041     /**
8042      * Select records.
8043      * @param {Array} records The records to select
8044      * @param {Boolean} keepExisting (optional) True to keep existing selections
8045      */
8046     selectRecords : function(records, keepExisting){
8047         if(!keepExisting){
8048             this.clearSelections();
8049         }
8050         var ds = this.grid.ds;
8051         for(var i = 0, len = records.length; i < len; i++){
8052             this.selectRow(ds.indexOf(records[i]), true);
8053         }
8054     },
8055
8056     /**
8057      * Gets the number of selected rows.
8058      * @return {Number}
8059      */
8060     getCount : function(){
8061         return this.selections.length;
8062     },
8063
8064     /**
8065      * Selects the first row in the grid.
8066      */
8067     selectFirstRow : function(){
8068         this.selectRow(0);
8069     },
8070
8071     /**
8072      * Select the last row.
8073      * @param {Boolean} keepExisting (optional) True to keep existing selections
8074      */
8075     selectLastRow : function(keepExisting){
8076         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8077     },
8078
8079     /**
8080      * Selects the row immediately following the last selected row.
8081      * @param {Boolean} keepExisting (optional) True to keep existing selections
8082      */
8083     selectNext : function(keepExisting){
8084         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085             this.selectRow(this.last+1, keepExisting);
8086             var view = this.grid.view ? this.grid.view : this.grid;
8087             view.focusRow(this.last);
8088         }
8089     },
8090
8091     /**
8092      * Selects the row that precedes the last selected row.
8093      * @param {Boolean} keepExisting (optional) True to keep existing selections
8094      */
8095     selectPrevious : function(keepExisting){
8096         if(this.last){
8097             this.selectRow(this.last-1, keepExisting);
8098             var view = this.grid.view ? this.grid.view : this.grid;
8099             view.focusRow(this.last);
8100         }
8101     },
8102
8103     /**
8104      * Returns the selected records
8105      * @return {Array} Array of selected records
8106      */
8107     getSelections : function(){
8108         return [].concat(this.selections.items);
8109     },
8110
8111     /**
8112      * Returns the first selected record.
8113      * @return {Record}
8114      */
8115     getSelected : function(){
8116         return this.selections.itemAt(0);
8117     },
8118
8119
8120     /**
8121      * Clears all selections.
8122      */
8123     clearSelections : function(fast){
8124         if(this.locked) {
8125             return;
8126         }
8127         if(fast !== true){
8128             var ds = this.grid.ds;
8129             var s = this.selections;
8130             s.each(function(r){
8131                 this.deselectRow(ds.indexOfId(r.id));
8132             }, this);
8133             s.clear();
8134         }else{
8135             this.selections.clear();
8136         }
8137         this.last = false;
8138     },
8139
8140
8141     /**
8142      * Selects all rows.
8143      */
8144     selectAll : function(){
8145         if(this.locked) {
8146             return;
8147         }
8148         this.selections.clear();
8149         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150             this.selectRow(i, true);
8151         }
8152     },
8153
8154     /**
8155      * Returns True if there is a selection.
8156      * @return {Boolean}
8157      */
8158     hasSelection : function(){
8159         return this.selections.length > 0;
8160     },
8161
8162     /**
8163      * Returns True if the specified row is selected.
8164      * @param {Number/Record} record The record or index of the record to check
8165      * @return {Boolean}
8166      */
8167     isSelected : function(index){
8168         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169         return (r && this.selections.key(r.id) ? true : false);
8170     },
8171
8172     /**
8173      * Returns True if the specified record id is selected.
8174      * @param {String} id The id of record to check
8175      * @return {Boolean}
8176      */
8177     isIdSelected : function(id){
8178         return (this.selections.key(id) ? true : false);
8179     },
8180
8181     // private
8182     handleMouseDown : function(e, t)
8183     {
8184         var view = this.grid.view ? this.grid.view : this.grid;
8185         var rowIndex;
8186         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8187             return;
8188         };
8189         if(e.shiftKey && this.last !== false){
8190             var last = this.last;
8191             this.selectRange(last, rowIndex, e.ctrlKey);
8192             this.last = last; // reset the last
8193             view.focusRow(rowIndex);
8194         }else{
8195             var isSelected = this.isSelected(rowIndex);
8196             if(e.button !== 0 && isSelected){
8197                 view.focusRow(rowIndex);
8198             }else if(e.ctrlKey && isSelected){
8199                 this.deselectRow(rowIndex);
8200             }else if(!isSelected){
8201                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202                 view.focusRow(rowIndex);
8203             }
8204         }
8205         this.fireEvent("afterselectionchange", this);
8206     },
8207     // private
8208     handleDragableRowClick :  function(grid, rowIndex, e) 
8209     {
8210         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211             this.selectRow(rowIndex, false);
8212             var view = this.grid.view ? this.grid.view : this.grid;
8213             view.focusRow(rowIndex);
8214              this.fireEvent("afterselectionchange", this);
8215         }
8216     },
8217     
8218     /**
8219      * Selects multiple rows.
8220      * @param {Array} rows Array of the indexes of the row to select
8221      * @param {Boolean} keepExisting (optional) True to keep existing selections
8222      */
8223     selectRows : function(rows, keepExisting){
8224         if(!keepExisting){
8225             this.clearSelections();
8226         }
8227         for(var i = 0, len = rows.length; i < len; i++){
8228             this.selectRow(rows[i], true);
8229         }
8230     },
8231
8232     /**
8233      * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234      * @param {Number} startRow The index of the first row in the range
8235      * @param {Number} endRow The index of the last row in the range
8236      * @param {Boolean} keepExisting (optional) True to retain existing selections
8237      */
8238     selectRange : function(startRow, endRow, keepExisting){
8239         if(this.locked) {
8240             return;
8241         }
8242         if(!keepExisting){
8243             this.clearSelections();
8244         }
8245         if(startRow <= endRow){
8246             for(var i = startRow; i <= endRow; i++){
8247                 this.selectRow(i, true);
8248             }
8249         }else{
8250             for(var i = startRow; i >= endRow; i--){
8251                 this.selectRow(i, true);
8252             }
8253         }
8254     },
8255
8256     /**
8257      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258      * @param {Number} startRow The index of the first row in the range
8259      * @param {Number} endRow The index of the last row in the range
8260      */
8261     deselectRange : function(startRow, endRow, preventViewNotify){
8262         if(this.locked) {
8263             return;
8264         }
8265         for(var i = startRow; i <= endRow; i++){
8266             this.deselectRow(i, preventViewNotify);
8267         }
8268     },
8269
8270     /**
8271      * Selects a row.
8272      * @param {Number} row The index of the row to select
8273      * @param {Boolean} keepExisting (optional) True to keep existing selections
8274      */
8275     selectRow : function(index, keepExisting, preventViewNotify){
8276         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8277             return;
8278         }
8279         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280             if(!keepExisting || this.singleSelect){
8281                 this.clearSelections();
8282             }
8283             var r = this.grid.ds.getAt(index);
8284             this.selections.add(r);
8285             this.last = this.lastActive = index;
8286             if(!preventViewNotify){
8287                 var view = this.grid.view ? this.grid.view : this.grid;
8288                 view.onRowSelect(index);
8289             }
8290             this.fireEvent("rowselect", this, index, r);
8291             this.fireEvent("selectionchange", this);
8292         }
8293     },
8294
8295     /**
8296      * Deselects a row.
8297      * @param {Number} row The index of the row to deselect
8298      */
8299     deselectRow : function(index, preventViewNotify){
8300         if(this.locked) {
8301             return;
8302         }
8303         if(this.last == index){
8304             this.last = false;
8305         }
8306         if(this.lastActive == index){
8307             this.lastActive = false;
8308         }
8309         var r = this.grid.ds.getAt(index);
8310         this.selections.remove(r);
8311         if(!preventViewNotify){
8312             var view = this.grid.view ? this.grid.view : this.grid;
8313             view.onRowDeselect(index);
8314         }
8315         this.fireEvent("rowdeselect", this, index);
8316         this.fireEvent("selectionchange", this);
8317     },
8318
8319     // private
8320     restoreLast : function(){
8321         if(this._last){
8322             this.last = this._last;
8323         }
8324     },
8325
8326     // private
8327     acceptsNav : function(row, col, cm){
8328         return !cm.isHidden(col) && cm.isCellEditable(col, row);
8329     },
8330
8331     // private
8332     onEditorKey : function(field, e){
8333         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8334         if(k == e.TAB){
8335             e.stopEvent();
8336             ed.completeEdit();
8337             if(e.shiftKey){
8338                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8339             }else{
8340                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8341             }
8342         }else if(k == e.ENTER && !e.ctrlKey){
8343             e.stopEvent();
8344             ed.completeEdit();
8345             if(e.shiftKey){
8346                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8347             }else{
8348                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8349             }
8350         }else if(k == e.ESC){
8351             ed.cancelEdit();
8352         }
8353         if(newCell){
8354             g.startEditing(newCell[0], newCell[1]);
8355         }
8356     }
8357 });/*
8358  * Based on:
8359  * Ext JS Library 1.1.1
8360  * Copyright(c) 2006-2007, Ext JS, LLC.
8361  *
8362  * Originally Released Under LGPL - original licence link has changed is not relivant.
8363  *
8364  * Fork - LGPL
8365  * <script type="text/javascript">
8366  */
8367  
8368
8369 /**
8370  * @class Roo.grid.ColumnModel
8371  * @extends Roo.util.Observable
8372  * This is the default implementation of a ColumnModel used by the Grid. It defines
8373  * the columns in the grid.
8374  * <br>Usage:<br>
8375  <pre><code>
8376  var colModel = new Roo.grid.ColumnModel([
8377         {header: "Ticker", width: 60, sortable: true, locked: true},
8378         {header: "Company Name", width: 150, sortable: true},
8379         {header: "Market Cap.", width: 100, sortable: true},
8380         {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381         {header: "Employees", width: 100, sortable: true, resizable: false}
8382  ]);
8383  </code></pre>
8384  * <p>
8385  
8386  * The config options listed for this class are options which may appear in each
8387  * individual column definition.
8388  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8389  * @constructor
8390  * @param {Object} config An Array of column config objects. See this class's
8391  * config objects for details.
8392 */
8393 Roo.grid.ColumnModel = function(config){
8394         /**
8395      * The config passed into the constructor
8396      */
8397     this.config = []; //config;
8398     this.lookup = {};
8399
8400     // if no id, create one
8401     // if the column does not have a dataIndex mapping,
8402     // map it to the order it is in the config
8403     for(var i = 0, len = config.length; i < len; i++){
8404         this.addColumn(config[i]);
8405         
8406     }
8407
8408     /**
8409      * The width of columns which have no width specified (defaults to 100)
8410      * @type Number
8411      */
8412     this.defaultWidth = 100;
8413
8414     /**
8415      * Default sortable of columns which have no sortable specified (defaults to false)
8416      * @type Boolean
8417      */
8418     this.defaultSortable = false;
8419
8420     this.addEvents({
8421         /**
8422              * @event widthchange
8423              * Fires when the width of a column changes.
8424              * @param {ColumnModel} this
8425              * @param {Number} columnIndex The column index
8426              * @param {Number} newWidth The new width
8427              */
8428             "widthchange": true,
8429         /**
8430              * @event headerchange
8431              * Fires when the text of a header changes.
8432              * @param {ColumnModel} this
8433              * @param {Number} columnIndex The column index
8434              * @param {Number} newText The new header text
8435              */
8436             "headerchange": true,
8437         /**
8438              * @event hiddenchange
8439              * Fires when a column is hidden or "unhidden".
8440              * @param {ColumnModel} this
8441              * @param {Number} columnIndex The column index
8442              * @param {Boolean} hidden true if hidden, false otherwise
8443              */
8444             "hiddenchange": true,
8445             /**
8446          * @event columnmoved
8447          * Fires when a column is moved.
8448          * @param {ColumnModel} this
8449          * @param {Number} oldIndex
8450          * @param {Number} newIndex
8451          */
8452         "columnmoved" : true,
8453         /**
8454          * @event columlockchange
8455          * Fires when a column's locked state is changed
8456          * @param {ColumnModel} this
8457          * @param {Number} colIndex
8458          * @param {Boolean} locked true if locked
8459          */
8460         "columnlockchange" : true
8461     });
8462     Roo.grid.ColumnModel.superclass.constructor.call(this);
8463 };
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8465     /**
8466      * @cfg {String} header [required] The header text to display in the Grid view.
8467      */
8468         /**
8469      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8470      */
8471         /**
8472      * @cfg {String} smHeader Header at Bootsrap Small width
8473      */
8474         /**
8475      * @cfg {String} mdHeader Header at Bootsrap Medium width
8476      */
8477         /**
8478      * @cfg {String} lgHeader Header at Bootsrap Large width
8479      */
8480         /**
8481      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8482      */
8483     /**
8484      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
8485      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486      * specified, the column's index is used as an index into the Record's data Array.
8487      */
8488     /**
8489      * @cfg {Number} width  The initial width in pixels of the column. Using this
8490      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8491      */
8492     /**
8493      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494      * Defaults to the value of the {@link #defaultSortable} property.
8495      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8496      */
8497     /**
8498      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
8499      */
8500     /**
8501      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
8502      */
8503     /**
8504      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
8505      */
8506     /**
8507      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
8508      */
8509     /**
8510      * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8514      */
8515        /**
8516      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
8517      */
8518     /**
8519      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
8520      */
8521     /**
8522      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
8523      */
8524     /**
8525      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8526      */
8527     /**
8528      * @cfg {String} tooltip mouse over tooltip text
8529      */
8530     /**
8531      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
8532      */
8533     /**
8534      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8535      */
8536     /**
8537      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8538      */
8539     /**
8540      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
8541      */
8542         /**
8543      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
8544      */
8545     /**
8546      * Returns the id of the column at the specified index.
8547      * @param {Number} index The column index
8548      * @return {String} the id
8549      */
8550     getColumnId : function(index){
8551         return this.config[index].id;
8552     },
8553
8554     /**
8555      * Returns the column for a specified id.
8556      * @param {String} id The column id
8557      * @return {Object} the column
8558      */
8559     getColumnById : function(id){
8560         return this.lookup[id];
8561     },
8562
8563     
8564     /**
8565      * Returns the column Object for a specified dataIndex.
8566      * @param {String} dataIndex The column dataIndex
8567      * @return {Object|Boolean} the column or false if not found
8568      */
8569     getColumnByDataIndex: function(dataIndex){
8570         var index = this.findColumnIndex(dataIndex);
8571         return index > -1 ? this.config[index] : false;
8572     },
8573     
8574     /**
8575      * Returns the index for a specified column id.
8576      * @param {String} id The column id
8577      * @return {Number} the index, or -1 if not found
8578      */
8579     getIndexById : function(id){
8580         for(var i = 0, len = this.config.length; i < len; i++){
8581             if(this.config[i].id == id){
8582                 return i;
8583             }
8584         }
8585         return -1;
8586     },
8587     
8588     /**
8589      * Returns the index for a specified column dataIndex.
8590      * @param {String} dataIndex The column dataIndex
8591      * @return {Number} the index, or -1 if not found
8592      */
8593     
8594     findColumnIndex : function(dataIndex){
8595         for(var i = 0, len = this.config.length; i < len; i++){
8596             if(this.config[i].dataIndex == dataIndex){
8597                 return i;
8598             }
8599         }
8600         return -1;
8601     },
8602     
8603     
8604     moveColumn : function(oldIndex, newIndex){
8605         var c = this.config[oldIndex];
8606         this.config.splice(oldIndex, 1);
8607         this.config.splice(newIndex, 0, c);
8608         this.dataMap = null;
8609         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8610     },
8611
8612     isLocked : function(colIndex){
8613         return this.config[colIndex].locked === true;
8614     },
8615
8616     setLocked : function(colIndex, value, suppressEvent){
8617         if(this.isLocked(colIndex) == value){
8618             return;
8619         }
8620         this.config[colIndex].locked = value;
8621         if(!suppressEvent){
8622             this.fireEvent("columnlockchange", this, colIndex, value);
8623         }
8624     },
8625
8626     getTotalLockedWidth : function(){
8627         var totalWidth = 0;
8628         for(var i = 0; i < this.config.length; i++){
8629             if(this.isLocked(i) && !this.isHidden(i)){
8630                 this.totalWidth += this.getColumnWidth(i);
8631             }
8632         }
8633         return totalWidth;
8634     },
8635
8636     getLockedCount : function(){
8637         for(var i = 0, len = this.config.length; i < len; i++){
8638             if(!this.isLocked(i)){
8639                 return i;
8640             }
8641         }
8642         
8643         return this.config.length;
8644     },
8645
8646     /**
8647      * Returns the number of columns.
8648      * @return {Number}
8649      */
8650     getColumnCount : function(visibleOnly){
8651         if(visibleOnly === true){
8652             var c = 0;
8653             for(var i = 0, len = this.config.length; i < len; i++){
8654                 if(!this.isHidden(i)){
8655                     c++;
8656                 }
8657             }
8658             return c;
8659         }
8660         return this.config.length;
8661     },
8662
8663     /**
8664      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665      * @param {Function} fn
8666      * @param {Object} scope (optional)
8667      * @return {Array} result
8668      */
8669     getColumnsBy : function(fn, scope){
8670         var r = [];
8671         for(var i = 0, len = this.config.length; i < len; i++){
8672             var c = this.config[i];
8673             if(fn.call(scope||this, c, i) === true){
8674                 r[r.length] = c;
8675             }
8676         }
8677         return r;
8678     },
8679
8680     /**
8681      * Returns true if the specified column is sortable.
8682      * @param {Number} col The column index
8683      * @return {Boolean}
8684      */
8685     isSortable : function(col){
8686         if(typeof this.config[col].sortable == "undefined"){
8687             return this.defaultSortable;
8688         }
8689         return this.config[col].sortable;
8690     },
8691
8692     /**
8693      * Returns the rendering (formatting) function defined for the column.
8694      * @param {Number} col The column index.
8695      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8696      */
8697     getRenderer : function(col){
8698         if(!this.config[col].renderer){
8699             return Roo.grid.ColumnModel.defaultRenderer;
8700         }
8701         return this.config[col].renderer;
8702     },
8703
8704     /**
8705      * Sets the rendering (formatting) function for a column.
8706      * @param {Number} col The column index
8707      * @param {Function} fn The function to use to process the cell's raw data
8708      * to return HTML markup for the grid view. The render function is called with
8709      * the following parameters:<ul>
8710      * <li>Data value.</li>
8711      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712      * <li>css A CSS style string to apply to the table cell.</li>
8713      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715      * <li>Row index</li>
8716      * <li>Column index</li>
8717      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8718      */
8719     setRenderer : function(col, fn){
8720         this.config[col].renderer = fn;
8721     },
8722
8723     /**
8724      * Returns the width for the specified column.
8725      * @param {Number} col The column index
8726      * @param (optional) {String} gridSize bootstrap width size.
8727      * @return {Number}
8728      */
8729     getColumnWidth : function(col, gridSize)
8730         {
8731                 var cfg = this.config[col];
8732                 
8733                 if (typeof(gridSize) == 'undefined') {
8734                         return cfg.width * 1 || this.defaultWidth;
8735                 }
8736                 if (gridSize === false) { // if we set it..
8737                         return cfg.width || false;
8738                 }
8739                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8740                 
8741                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8743                                 continue;
8744                         }
8745                         return cfg[ sizes[i] ];
8746                 }
8747                 return 1;
8748                 
8749     },
8750
8751     /**
8752      * Sets the width for a column.
8753      * @param {Number} col The column index
8754      * @param {Number} width The new width
8755      */
8756     setColumnWidth : function(col, width, suppressEvent){
8757         this.config[col].width = width;
8758         this.totalWidth = null;
8759         if(!suppressEvent){
8760              this.fireEvent("widthchange", this, col, width);
8761         }
8762     },
8763
8764     /**
8765      * Returns the total width of all columns.
8766      * @param {Boolean} includeHidden True to include hidden column widths
8767      * @return {Number}
8768      */
8769     getTotalWidth : function(includeHidden){
8770         if(!this.totalWidth){
8771             this.totalWidth = 0;
8772             for(var i = 0, len = this.config.length; i < len; i++){
8773                 if(includeHidden || !this.isHidden(i)){
8774                     this.totalWidth += this.getColumnWidth(i);
8775                 }
8776             }
8777         }
8778         return this.totalWidth;
8779     },
8780
8781     /**
8782      * Returns the header for the specified column.
8783      * @param {Number} col The column index
8784      * @return {String}
8785      */
8786     getColumnHeader : function(col){
8787         return this.config[col].header;
8788     },
8789
8790     /**
8791      * Sets the header for a column.
8792      * @param {Number} col The column index
8793      * @param {String} header The new header
8794      */
8795     setColumnHeader : function(col, header){
8796         this.config[col].header = header;
8797         this.fireEvent("headerchange", this, col, header);
8798     },
8799
8800     /**
8801      * Returns the tooltip for the specified column.
8802      * @param {Number} col The column index
8803      * @return {String}
8804      */
8805     getColumnTooltip : function(col){
8806             return this.config[col].tooltip;
8807     },
8808     /**
8809      * Sets the tooltip for a column.
8810      * @param {Number} col The column index
8811      * @param {String} tooltip The new tooltip
8812      */
8813     setColumnTooltip : function(col, tooltip){
8814             this.config[col].tooltip = tooltip;
8815     },
8816
8817     /**
8818      * Returns the dataIndex for the specified column.
8819      * @param {Number} col The column index
8820      * @return {Number}
8821      */
8822     getDataIndex : function(col){
8823         return this.config[col].dataIndex;
8824     },
8825
8826     /**
8827      * Sets the dataIndex for a column.
8828      * @param {Number} col The column index
8829      * @param {Number} dataIndex The new dataIndex
8830      */
8831     setDataIndex : function(col, dataIndex){
8832         this.config[col].dataIndex = dataIndex;
8833     },
8834
8835     
8836     
8837     /**
8838      * Returns true if the cell is editable.
8839      * @param {Number} colIndex The column index
8840      * @param {Number} rowIndex The row index - this is nto actually used..?
8841      * @return {Boolean}
8842      */
8843     isCellEditable : function(colIndex, rowIndex){
8844         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8845     },
8846
8847     /**
8848      * Returns the editor defined for the cell/column.
8849      * return false or null to disable editing.
8850      * @param {Number} colIndex The column index
8851      * @param {Number} rowIndex The row index
8852      * @return {Object}
8853      */
8854     getCellEditor : function(colIndex, rowIndex){
8855         return this.config[colIndex].editor;
8856     },
8857
8858     /**
8859      * Sets if a column is editable.
8860      * @param {Number} col The column index
8861      * @param {Boolean} editable True if the column is editable
8862      */
8863     setEditable : function(col, editable){
8864         this.config[col].editable = editable;
8865     },
8866
8867
8868     /**
8869      * Returns true if the column is hidden.
8870      * @param {Number} colIndex The column index
8871      * @return {Boolean}
8872      */
8873     isHidden : function(colIndex){
8874         return this.config[colIndex].hidden;
8875     },
8876
8877
8878     /**
8879      * Returns true if the column width cannot be changed
8880      */
8881     isFixed : function(colIndex){
8882         return this.config[colIndex].fixed;
8883     },
8884
8885     /**
8886      * Returns true if the column can be resized
8887      * @return {Boolean}
8888      */
8889     isResizable : function(colIndex){
8890         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8891     },
8892     /**
8893      * Sets if a column is hidden.
8894      * @param {Number} colIndex The column index
8895      * @param {Boolean} hidden True if the column is hidden
8896      */
8897     setHidden : function(colIndex, hidden){
8898         this.config[colIndex].hidden = hidden;
8899         this.totalWidth = null;
8900         this.fireEvent("hiddenchange", this, colIndex, hidden);
8901     },
8902
8903     /**
8904      * Sets the editor for a column.
8905      * @param {Number} col The column index
8906      * @param {Object} editor The editor object
8907      */
8908     setEditor : function(col, editor){
8909         this.config[col].editor = editor;
8910     },
8911     /**
8912      * Add a column (experimental...) - defaults to adding to the end..
8913      * @param {Object} config 
8914     */
8915     addColumn : function(c)
8916     {
8917     
8918         var i = this.config.length;
8919         this.config[i] = c;
8920         
8921         if(typeof c.dataIndex == "undefined"){
8922             c.dataIndex = i;
8923         }
8924         if(typeof c.renderer == "string"){
8925             c.renderer = Roo.util.Format[c.renderer];
8926         }
8927         if(typeof c.id == "undefined"){
8928             c.id = Roo.id();
8929         }
8930         if(c.editor && c.editor.xtype){
8931             c.editor  = Roo.factory(c.editor, Roo.grid);
8932         }
8933         if(c.editor && c.editor.isFormField){
8934             c.editor = new Roo.grid.GridEditor(c.editor);
8935         }
8936         this.lookup[c.id] = c;
8937     }
8938     
8939 });
8940
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8942 {
8943     if(typeof value == "object") {
8944         return value;
8945     }
8946         if(typeof value == "string" && value.length < 1){
8947             return "&#160;";
8948         }
8949     
8950         return String.format("{0}", value);
8951 };
8952
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8955 /*
8956  * Based on:
8957  * Ext JS Library 1.1.1
8958  * Copyright(c) 2006-2007, Ext JS, LLC.
8959  *
8960  * Originally Released Under LGPL - original licence link has changed is not relivant.
8961  *
8962  * Fork - LGPL
8963  * <script type="text/javascript">
8964  */
8965  
8966 /**
8967  * @class Roo.LoadMask
8968  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8969  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8971  * element's UpdateManager load indicator and will be destroyed after the initial load.
8972  * @constructor
8973  * Create a new LoadMask
8974  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975  * @param {Object} config The config object
8976  */
8977 Roo.LoadMask = function(el, config){
8978     this.el = Roo.get(el);
8979     Roo.apply(this, config);
8980     if(this.store){
8981         this.store.on('beforeload', this.onBeforeLoad, this);
8982         this.store.on('load', this.onLoad, this);
8983         this.store.on('loadexception', this.onLoadException, this);
8984         this.removeMask = false;
8985     }else{
8986         var um = this.el.getUpdateManager();
8987         um.showLoadIndicator = false; // disable the default indicator
8988         um.on('beforeupdate', this.onBeforeLoad, this);
8989         um.on('update', this.onLoad, this);
8990         um.on('failure', this.onLoad, this);
8991         this.removeMask = true;
8992     }
8993 };
8994
8995 Roo.LoadMask.prototype = {
8996     /**
8997      * @cfg {Boolean} removeMask
8998      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
9000      */
9001     removeMask : false,
9002     /**
9003      * @cfg {String} msg
9004      * The text to display in a centered loading message box (defaults to 'Loading...')
9005      */
9006     msg : 'Loading...',
9007     /**
9008      * @cfg {String} msgCls
9009      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9010      */
9011     msgCls : 'x-mask-loading',
9012
9013     /**
9014      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9015      * @type Boolean
9016      */
9017     disabled: false,
9018
9019     /**
9020      * Disables the mask to prevent it from being displayed
9021      */
9022     disable : function(){
9023        this.disabled = true;
9024     },
9025
9026     /**
9027      * Enables the mask so that it can be displayed
9028      */
9029     enable : function(){
9030         this.disabled = false;
9031     },
9032     
9033     onLoadException : function()
9034     {
9035         Roo.log(arguments);
9036         
9037         if (typeof(arguments[3]) != 'undefined') {
9038             Roo.MessageBox.alert("Error loading",arguments[3]);
9039         } 
9040         /*
9041         try {
9042             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9044             }   
9045         } catch(e) {
9046             
9047         }
9048         */
9049     
9050         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9051     },
9052     // private
9053     onLoad : function()
9054     {
9055         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056     },
9057
9058     // private
9059     onBeforeLoad : function(){
9060         if(!this.disabled){
9061             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9062         }
9063     },
9064
9065     // private
9066     destroy : function(){
9067         if(this.store){
9068             this.store.un('beforeload', this.onBeforeLoad, this);
9069             this.store.un('load', this.onLoad, this);
9070             this.store.un('loadexception', this.onLoadException, this);
9071         }else{
9072             var um = this.el.getUpdateManager();
9073             um.un('beforeupdate', this.onBeforeLoad, this);
9074             um.un('update', this.onLoad, this);
9075             um.un('failure', this.onLoad, this);
9076         }
9077     }
9078 };/**
9079  * @class Roo.bootstrap.Table
9080  * @licence LGBL
9081  * @extends Roo.bootstrap.Component
9082  * @children Roo.bootstrap.TableBody
9083  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
9084  * Similar to Roo.grid.Grid
9085  * <pre><code>
9086  var table = Roo.factory({
9087     xtype : 'Table',
9088     xns : Roo.bootstrap,
9089     autoSizeColumns: true,
9090     
9091     
9092     store : {
9093         xtype : 'Store',
9094         xns : Roo.data,
9095         remoteSort : true,
9096         sortInfo : { direction : 'ASC', field: 'name' },
9097         proxy : {
9098            xtype : 'HttpProxy',
9099            xns : Roo.data,
9100            method : 'GET',
9101            url : 'https://example.com/some.data.url.json'
9102         },
9103         reader : {
9104            xtype : 'JsonReader',
9105            xns : Roo.data,
9106            fields : [ 'id', 'name', whatever' ],
9107            id : 'id',
9108            root : 'data'
9109         }
9110     },
9111     cm : [
9112         {
9113             xtype : 'ColumnModel',
9114             xns : Roo.grid,
9115             align : 'center',
9116             cursor : 'pointer',
9117             dataIndex : 'is_in_group',
9118             header : "Name",
9119             sortable : true,
9120             renderer : function(v, x , r) {  
9121             
9122                 return String.format("{0}", v)
9123             }
9124             width : 3
9125         } // more columns..
9126     ],
9127     selModel : {
9128         xtype : 'RowSelectionModel',
9129         xns : Roo.bootstrap.Table
9130         // you can add listeners to catch selection change here....
9131     }
9132      
9133
9134  });
9135  // set any options
9136  grid.render(Roo.get("some-div"));
9137 </code></pre>
9138
9139 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
9140
9141
9142
9143  *
9144  * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145  * @cfg {Roo.data.Store} store The data store to use
9146  * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9147  * 
9148  * @cfg {String} cls table class
9149  *
9150  *
9151  * @cfg {string} empty_results  Text to display for no results 
9152  * @cfg {boolean} striped Should the rows be alternative striped
9153  * @cfg {boolean} bordered Add borders to the table
9154  * @cfg {boolean} hover Add hover highlighting
9155  * @cfg {boolean} condensed Format condensed
9156  * @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,
9157  *                also adds table-responsive (see bootstrap docs for details)
9158  * @cfg {Boolean} loadMask (true|false) default false
9159  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160  * @cfg {Boolean} headerShow (true|false) generate thead, default true
9161  * @cfg {Boolean} rowSelection (true|false) default false
9162  * @cfg {Boolean} cellSelection (true|false) default false
9163  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9164  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
9165  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
9166  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
9167  * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9168  * @cfg {Boolean} disableAutoSize disable auto size
9169  *
9170  * 
9171  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
9172  * 
9173  * @constructor
9174  * Create a new Table
9175  * @param {Object} config The config object
9176  */
9177
9178 Roo.bootstrap.Table = function(config)
9179 {
9180     Roo.bootstrap.Table.superclass.constructor.call(this, config);
9181      
9182     // BC...
9183     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9184     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9185     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9186     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9187     
9188     this.view = this; // compat with grid.
9189     
9190     this.sm = this.sm || {xtype: 'RowSelectionModel'};
9191     if (this.sm) {
9192         this.sm.grid = this;
9193         this.selModel = Roo.factory(this.sm, Roo.grid);
9194         this.sm = this.selModel;
9195         this.sm.xmodule = this.xmodule || false;
9196     }
9197     
9198     if (this.cm && typeof(this.cm.config) == 'undefined') {
9199         this.colModel = new Roo.grid.ColumnModel(this.cm);
9200         this.cm = this.colModel;
9201         this.cm.xmodule = this.xmodule || false;
9202     }
9203     if (this.store) {
9204         this.store= Roo.factory(this.store, Roo.data);
9205         this.ds = this.store;
9206         this.ds.xmodule = this.xmodule || false;
9207          
9208     }
9209     if (this.footer && this.store) {
9210         this.footer.dataSource = this.ds;
9211         this.footer = Roo.factory(this.footer);
9212     }
9213     
9214     /** @private */
9215     this.addEvents({
9216         /**
9217          * @event cellclick
9218          * Fires when a cell is clicked
9219          * @param {Roo.bootstrap.Table} this
9220          * @param {Roo.Element} el
9221          * @param {Number} rowIndex
9222          * @param {Number} columnIndex
9223          * @param {Roo.EventObject} e
9224          */
9225         "cellclick" : true,
9226         /**
9227          * @event celldblclick
9228          * Fires when a cell is double clicked
9229          * @param {Roo.bootstrap.Table} this
9230          * @param {Roo.Element} el
9231          * @param {Number} rowIndex
9232          * @param {Number} columnIndex
9233          * @param {Roo.EventObject} e
9234          */
9235         "celldblclick" : true,
9236         /**
9237          * @event rowclick
9238          * Fires when a row is clicked
9239          * @param {Roo.bootstrap.Table} this
9240          * @param {Roo.Element} el
9241          * @param {Number} rowIndex
9242          * @param {Roo.EventObject} e
9243          */
9244         "rowclick" : true,
9245         /**
9246          * @event rowdblclick
9247          * Fires when a row is double clicked
9248          * @param {Roo.bootstrap.Table} this
9249          * @param {Roo.Element} el
9250          * @param {Number} rowIndex
9251          * @param {Roo.EventObject} e
9252          */
9253         "rowdblclick" : true,
9254         /**
9255          * @event mouseover
9256          * Fires when a mouseover occur
9257          * @param {Roo.bootstrap.Table} this
9258          * @param {Roo.Element} el
9259          * @param {Number} rowIndex
9260          * @param {Number} columnIndex
9261          * @param {Roo.EventObject} e
9262          */
9263         "mouseover" : true,
9264         /**
9265          * @event mouseout
9266          * Fires when a mouseout occur
9267          * @param {Roo.bootstrap.Table} this
9268          * @param {Roo.Element} el
9269          * @param {Number} rowIndex
9270          * @param {Number} columnIndex
9271          * @param {Roo.EventObject} e
9272          */
9273         "mouseout" : true,
9274         /**
9275          * @event rowclass
9276          * Fires when a row is rendered, so you can change add a style to it.
9277          * @param {Roo.bootstrap.Table} this
9278          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
9279          */
9280         'rowclass' : true,
9281           /**
9282          * @event rowsrendered
9283          * Fires when all the  rows have been rendered
9284          * @param {Roo.bootstrap.Table} this
9285          */
9286         'rowsrendered' : true,
9287         /**
9288          * @event contextmenu
9289          * The raw contextmenu event for the entire grid.
9290          * @param {Roo.EventObject} e
9291          */
9292         "contextmenu" : true,
9293         /**
9294          * @event rowcontextmenu
9295          * Fires when a row is right clicked
9296          * @param {Roo.bootstrap.Table} this
9297          * @param {Number} rowIndex
9298          * @param {Roo.EventObject} e
9299          */
9300         "rowcontextmenu" : true,
9301         /**
9302          * @event cellcontextmenu
9303          * Fires when a cell is right clicked
9304          * @param {Roo.bootstrap.Table} this
9305          * @param {Number} rowIndex
9306          * @param {Number} cellIndex
9307          * @param {Roo.EventObject} e
9308          */
9309          "cellcontextmenu" : true,
9310          /**
9311          * @event headercontextmenu
9312          * Fires when a header is right clicked
9313          * @param {Roo.bootstrap.Table} this
9314          * @param {Number} columnIndex
9315          * @param {Roo.EventObject} e
9316          */
9317         "headercontextmenu" : true,
9318         /**
9319          * @event mousedown
9320          * The raw mousedown event for the entire grid.
9321          * @param {Roo.EventObject} e
9322          */
9323         "mousedown" : true
9324         
9325     });
9326 };
9327
9328 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
9329     
9330     cls: false,
9331     
9332     empty_results : '',
9333     striped : false,
9334     scrollBody : false,
9335     bordered: false,
9336     hover:  false,
9337     condensed : false,
9338     responsive : false,
9339     sm : false,
9340     cm : false,
9341     store : false,
9342     loadMask : false,
9343     footerShow : true,
9344     headerShow : true,
9345     enableColumnResize: true,
9346     disableAutoSize: false,
9347   
9348     rowSelection : false,
9349     cellSelection : false,
9350     layout : false,
9351
9352     minColumnWidth : 50,
9353     
9354     // Roo.Element - the tbody
9355     bodyEl: false,  // <tbody> Roo.Element - thead element    
9356     headEl: false,  // <thead> Roo.Element - thead element
9357     resizeProxy : false, // proxy element for dragging?
9358
9359
9360     
9361     container: false, // used by gridpanel...
9362     
9363     lazyLoad : false,
9364     
9365     CSS : Roo.util.CSS,
9366     
9367     auto_hide_footer : false,
9368     
9369     view: false, // actually points to this..
9370     
9371     getAutoCreate : function()
9372     {
9373         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9374         
9375         cfg = {
9376             tag: 'table',
9377             cls : 'table', 
9378             cn : []
9379         };
9380         // this get's auto added by panel.Grid
9381         if (this.scrollBody) {
9382             cfg.cls += ' table-body-fixed';
9383         }    
9384         if (this.striped) {
9385             cfg.cls += ' table-striped';
9386         }
9387         
9388         if (this.hover) {
9389             cfg.cls += ' table-hover';
9390         }
9391         if (this.bordered) {
9392             cfg.cls += ' table-bordered';
9393         }
9394         if (this.condensed) {
9395             cfg.cls += ' table-condensed';
9396         }
9397         
9398         if (this.responsive) {
9399             cfg.cls += ' table-responsive';
9400         }
9401         
9402         if (this.cls) {
9403             cfg.cls+=  ' ' +this.cls;
9404         }
9405         
9406         
9407         
9408         if (this.layout) {
9409             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9410         }
9411         
9412         if(this.store || this.cm){
9413             if(this.headerShow){
9414                 cfg.cn.push(this.renderHeader());
9415             }
9416             
9417             cfg.cn.push(this.renderBody());
9418             
9419             if(this.footerShow){
9420                 cfg.cn.push(this.renderFooter());
9421             }
9422             // where does this come from?
9423             //cfg.cls+=  ' TableGrid';
9424         }
9425         
9426         return { cn : [ cfg ] };
9427     },
9428     
9429     initEvents : function()
9430     {   
9431         if(!this.store || !this.cm){
9432             return;
9433         }
9434         if (this.selModel) {
9435             this.selModel.initEvents();
9436         }
9437         
9438         
9439         //Roo.log('initEvents with ds!!!!');
9440         
9441         this.bodyEl = this.el.select('tbody', true).first();
9442         this.headEl = this.el.select('thead', true).first();
9443         this.mainFoot = this.el.select('tfoot', true).first();
9444         
9445         
9446         
9447         
9448         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449             e.on('click', this.sort, this);
9450         }, this);
9451         
9452         
9453         // why is this done????? = it breaks dialogs??
9454         //this.parent().el.setStyle('position', 'relative');
9455         
9456         
9457         if (this.footer) {
9458             this.footer.parentId = this.id;
9459             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9460             
9461             if(this.lazyLoad){
9462                 this.el.select('tfoot tr td').first().addClass('hide');
9463             }
9464         } 
9465         
9466         if(this.loadMask) {
9467             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9468         }
9469         
9470         this.store.on('load', this.onLoad, this);
9471         this.store.on('beforeload', this.onBeforeLoad, this);
9472         this.store.on('update', this.onUpdate, this);
9473         this.store.on('add', this.onAdd, this);
9474         this.store.on("clear", this.clear, this);
9475         
9476         this.el.on("contextmenu", this.onContextMenu, this);
9477         
9478         
9479         this.cm.on("headerchange", this.onHeaderChange, this);
9480         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9481
9482  //?? does bodyEl get replaced on render?
9483         this.bodyEl.on("click", this.onClick, this);
9484         this.bodyEl.on("dblclick", this.onDblClick, this);        
9485         this.bodyEl.on('scroll', this.onBodyScroll, this);
9486
9487         // guessing mainbody will work - this relays usually caught by selmodel at present.
9488         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9489   
9490   
9491         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9492         
9493   
9494         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9496         }
9497         
9498         this.initCSS();
9499     },
9500     // Compatibility with grid - we implement all the view features at present.
9501     getView : function()
9502     {
9503         return this;
9504     },
9505     
9506     initCSS : function()
9507     {
9508         if(this.disableAutoSize) {
9509             Roo.log("DISABLEINITCSS");
9510             return;
9511         }
9512         
9513         var cm = this.cm, styles = [];
9514         this.CSS.removeStyleSheet(this.id + '-cssrules');
9515         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9516         // we can honour xs/sm/md/xl  as widths...
9517         // we first have to decide what widht we are currently at...
9518         var sz = Roo.getGridSize();
9519         
9520         var total = 0;
9521         var last = -1;
9522         var cols = []; // visable cols.
9523         var total_abs = 0;
9524         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9525             var w = cm.getColumnWidth(i, false);
9526             if(cm.isHidden(i)){
9527                 cols.push( { rel : false, abs : 0 });
9528                 continue;
9529             }
9530             if (w !== false) {
9531                 cols.push( { rel : false, abs : w });
9532                 total_abs += w;
9533                 last = i; // not really..
9534                 continue;
9535             }
9536             var w = cm.getColumnWidth(i, sz);
9537             if (w > 0) {
9538                 last = i
9539             }
9540             total += w;
9541             cols.push( { rel : w, abs : false });
9542         }
9543         
9544         var avail = this.bodyEl.dom.clientWidth - total_abs;
9545         
9546         var unitWidth = Math.floor(avail / total);
9547         var rem = avail - (unitWidth * total);
9548         
9549         var hidden, width, pos = 0 , splithide , left;
9550         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9551             
9552             hidden = 'display:none;';
9553             left = '';
9554             width  = 'width:0px;';
9555             splithide = '';
9556             if(!cm.isHidden(i)){
9557                 hidden = '';
9558                 
9559                 
9560                 // we can honour xs/sm/md/xl ?
9561                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9562                 if (w===0) {
9563                     hidden = 'display:none;';
9564                 }
9565                 // width should return a small number...
9566                 if (i == last) {
9567                     w+=rem; // add the remaining with..
9568                 }
9569                 pos += w;
9570                 left = "left:" + (pos -4) + "px;";
9571                 width = "width:" + w+ "px;";
9572                 
9573             }
9574             if (this.responsive) {
9575                 width = '';
9576                 left = '';
9577                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9578                 splithide = 'display: none;';
9579             }
9580             
9581             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9582             if (this.headEl) {
9583                 if (i == last) {
9584                     splithide = 'display:none;';
9585                 }
9586                 
9587                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9588                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9589                             // this is the popover version..
9590                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9591                 );
9592             }
9593             
9594         }
9595         //Roo.log(styles.join(''));
9596         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9597         
9598     },
9599     
9600     
9601     
9602     onContextMenu : function(e, t)
9603     {
9604         this.processEvent("contextmenu", e);
9605     },
9606     
9607     processEvent : function(name, e)
9608     {
9609         if (name != 'touchstart' ) {
9610             this.fireEvent(name, e);    
9611         }
9612         
9613         var t = e.getTarget();
9614         
9615         var cell = Roo.get(t);
9616         
9617         if(!cell){
9618             return;
9619         }
9620         
9621         if(cell.findParent('tfoot', false, true)){
9622             return;
9623         }
9624         
9625         if(cell.findParent('thead', false, true)){
9626             
9627             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9628                 cell = Roo.get(t).findParent('th', false, true);
9629                 if (!cell) {
9630                     Roo.log("failed to find th in thead?");
9631                     Roo.log(e.getTarget());
9632                     return;
9633                 }
9634             }
9635             
9636             var cellIndex = cell.dom.cellIndex;
9637             
9638             var ename = name == 'touchstart' ? 'click' : name;
9639             this.fireEvent("header" + ename, this, cellIndex, e);
9640             
9641             return;
9642         }
9643         
9644         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9645             cell = Roo.get(t).findParent('td', false, true);
9646             if (!cell) {
9647                 Roo.log("failed to find th in tbody?");
9648                 Roo.log(e.getTarget());
9649                 return;
9650             }
9651         }
9652         
9653         var row = cell.findParent('tr', false, true);
9654         var cellIndex = cell.dom.cellIndex;
9655         var rowIndex = row.dom.rowIndex - 1;
9656         
9657         if(row !== false){
9658             
9659             this.fireEvent("row" + name, this, rowIndex, e);
9660             
9661             if(cell !== false){
9662             
9663                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9664             }
9665         }
9666         
9667     },
9668     
9669     onMouseover : function(e, el)
9670     {
9671         var cell = Roo.get(el);
9672         
9673         if(!cell){
9674             return;
9675         }
9676         
9677         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9678             cell = cell.findParent('td', false, true);
9679         }
9680         
9681         var row = cell.findParent('tr', false, true);
9682         var cellIndex = cell.dom.cellIndex;
9683         var rowIndex = row.dom.rowIndex - 1; // start from 0
9684         
9685         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9686         
9687     },
9688     
9689     onMouseout : function(e, el)
9690     {
9691         var cell = Roo.get(el);
9692         
9693         if(!cell){
9694             return;
9695         }
9696         
9697         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9698             cell = cell.findParent('td', false, true);
9699         }
9700         
9701         var row = cell.findParent('tr', false, true);
9702         var cellIndex = cell.dom.cellIndex;
9703         var rowIndex = row.dom.rowIndex - 1; // start from 0
9704         
9705         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9706         
9707     },
9708     
9709     onClick : function(e, el)
9710     {
9711         var cell = Roo.get(el);
9712         
9713         if(!cell || (!this.cellSelection && !this.rowSelection)){
9714             return;
9715         }
9716         
9717         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9718             cell = cell.findParent('td', false, true);
9719         }
9720         
9721         if(!cell || typeof(cell) == 'undefined'){
9722             return;
9723         }
9724         
9725         var row = cell.findParent('tr', false, true);
9726         
9727         if(!row || typeof(row) == 'undefined'){
9728             return;
9729         }
9730         
9731         var cellIndex = cell.dom.cellIndex;
9732         var rowIndex = this.getRowIndex(row);
9733         
9734         // why??? - should these not be based on SelectionModel?
9735         //if(this.cellSelection){
9736             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9737         //}
9738         
9739         //if(this.rowSelection){
9740             this.fireEvent('rowclick', this, row, rowIndex, e);
9741         //}
9742          
9743     },
9744         
9745     onDblClick : function(e,el)
9746     {
9747         var cell = Roo.get(el);
9748         
9749         if(!cell || (!this.cellSelection && !this.rowSelection)){
9750             return;
9751         }
9752         
9753         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9754             cell = cell.findParent('td', false, true);
9755         }
9756         
9757         if(!cell || typeof(cell) == 'undefined'){
9758             return;
9759         }
9760         
9761         var row = cell.findParent('tr', false, true);
9762         
9763         if(!row || typeof(row) == 'undefined'){
9764             return;
9765         }
9766         
9767         var cellIndex = cell.dom.cellIndex;
9768         var rowIndex = this.getRowIndex(row);
9769         
9770         if(this.cellSelection){
9771             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9772         }
9773         
9774         if(this.rowSelection){
9775             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9776         }
9777     },
9778     findRowIndex : function(el)
9779     {
9780         var cell = Roo.get(el);
9781         if(!cell) {
9782             return false;
9783         }
9784         var row = cell.findParent('tr', false, true);
9785         
9786         if(!row || typeof(row) == 'undefined'){
9787             return false;
9788         }
9789         return this.getRowIndex(row);
9790     },
9791     sort : function(e,el)
9792     {
9793         var col = Roo.get(el);
9794         
9795         if(!col.hasClass('sortable')){
9796             return;
9797         }
9798         
9799         var sort = col.attr('sort');
9800         var dir = 'ASC';
9801         
9802         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9803             dir = 'DESC';
9804         }
9805         
9806         this.store.sortInfo = {field : sort, direction : dir};
9807         
9808         if (this.footer) {
9809             Roo.log("calling footer first");
9810             this.footer.onClick('first');
9811         } else {
9812         
9813             this.store.load({ params : { start : 0 } });
9814         }
9815     },
9816     
9817     renderHeader : function()
9818     {
9819         var header = {
9820             tag: 'thead',
9821             cn : []
9822         };
9823         
9824         var cm = this.cm;
9825         this.totalWidth = 0;
9826         
9827         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9828             
9829             var config = cm.config[i];
9830             
9831             var c = {
9832                 tag: 'th',
9833                 cls : 'x-hcol-' + i,
9834                 style : '',
9835                 
9836                 html: cm.getColumnHeader(i)
9837             };
9838             
9839             var tooltip = cm.getColumnTooltip(i);
9840             if (tooltip) {
9841                 c.tooltip = tooltip;
9842             }
9843             
9844             
9845             var hh = '';
9846             
9847             if(typeof(config.sortable) != 'undefined' && config.sortable){
9848                 c.cls += ' sortable';
9849                 c.html = '<i class="fa"></i>' + c.html;
9850             }
9851             
9852             // could use BS4 hidden-..-down 
9853             
9854             if(typeof(config.lgHeader) != 'undefined'){
9855                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9856             }
9857             
9858             if(typeof(config.mdHeader) != 'undefined'){
9859                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9860             }
9861             
9862             if(typeof(config.smHeader) != 'undefined'){
9863                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9864             }
9865             
9866             if(typeof(config.xsHeader) != 'undefined'){
9867                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9868             }
9869             
9870             if(hh.length){
9871                 c.html = hh;
9872             }
9873             
9874             if(typeof(config.tooltip) != 'undefined'){
9875                 c.tooltip = config.tooltip;
9876             }
9877             
9878             if(typeof(config.colspan) != 'undefined'){
9879                 c.colspan = config.colspan;
9880             }
9881             
9882             // hidden is handled by CSS now
9883             
9884             if(typeof(config.dataIndex) != 'undefined'){
9885                 c.sort = config.dataIndex;
9886             }
9887             
9888            
9889             
9890             if(typeof(config.align) != 'undefined' && config.align.length){
9891                 c.style += ' text-align:' + config.align + ';';
9892             }
9893             
9894             /* width is done in CSS
9895              *if(typeof(config.width) != 'undefined'){
9896                 c.style += ' width:' + config.width + 'px;';
9897                 this.totalWidth += config.width;
9898             } else {
9899                 this.totalWidth += 100; // assume minimum of 100 per column?
9900             }
9901             */
9902             
9903             if(typeof(config.cls) != 'undefined'){
9904                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9905             }
9906             // this is the bit that doesnt reall work at all...
9907             
9908             if (this.responsive) {
9909                  
9910             
9911                 ['xs','sm','md','lg'].map(function(size){
9912                     
9913                     if(typeof(config[size]) == 'undefined'){
9914                         return;
9915                     }
9916                      
9917                     if (!config[size]) { // 0 = hidden
9918                         // BS 4 '0' is treated as hide that column and below.
9919                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9920                         return;
9921                     }
9922                     
9923                     c.cls += ' col-' + size + '-' + config[size] + (
9924                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9925                     );
9926                     
9927                     
9928                 });
9929             }
9930             // at the end?
9931             
9932             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9933             
9934             
9935             
9936             
9937             header.cn.push(c)
9938         }
9939         
9940         return header;
9941     },
9942     
9943     renderBody : function()
9944     {
9945         var body = {
9946             tag: 'tbody',
9947             cn : [
9948                 {
9949                     tag: 'tr',
9950                     cn : [
9951                         {
9952                             tag : 'td',
9953                             colspan :  this.cm.getColumnCount()
9954                         }
9955                     ]
9956                 }
9957             ]
9958         };
9959         
9960         return body;
9961     },
9962     
9963     renderFooter : function()
9964     {
9965         var footer = {
9966             tag: 'tfoot',
9967             cn : [
9968                 {
9969                     tag: 'tr',
9970                     cn : [
9971                         {
9972                             tag : 'td',
9973                             colspan :  this.cm.getColumnCount()
9974                         }
9975                     ]
9976                 }
9977             ]
9978         };
9979         
9980         return footer;
9981     },
9982     
9983     
9984     
9985     onLoad : function()
9986     {
9987 //        Roo.log('ds onload');
9988         this.clear();
9989         
9990         var _this = this;
9991         var cm = this.cm;
9992         var ds = this.store;
9993         
9994         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9995             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9996             if (_this.store.sortInfo) {
9997                     
9998                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9999                     e.select('i', true).addClass(['fa-arrow-up']);
10000                 }
10001                 
10002                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10003                     e.select('i', true).addClass(['fa-arrow-down']);
10004                 }
10005             }
10006         });
10007         
10008         var tbody =  this.bodyEl;
10009               
10010         if(ds.getCount() > 0){
10011             ds.data.each(function(d,rowIndex){
10012                 var row =  this.renderRow(cm, ds, rowIndex);
10013                 
10014                 tbody.createChild(row);
10015                 
10016                 var _this = this;
10017                 
10018                 if(row.cellObjects.length){
10019                     Roo.each(row.cellObjects, function(r){
10020                         _this.renderCellObject(r);
10021                     })
10022                 }
10023                 
10024             }, this);
10025         } else if (this.empty_results.length) {
10026             this.el.mask(this.empty_results, 'no-spinner');
10027         }
10028         
10029         var tfoot = this.el.select('tfoot', true).first();
10030         
10031         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10032             
10033             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10034             
10035             var total = this.ds.getTotalCount();
10036             
10037             if(this.footer.pageSize < total){
10038                 this.mainFoot.show();
10039             }
10040         }
10041         
10042         Roo.each(this.el.select('tbody td', true).elements, function(e){
10043             e.on('mouseover', _this.onMouseover, _this);
10044         });
10045         
10046         Roo.each(this.el.select('tbody td', true).elements, function(e){
10047             e.on('mouseout', _this.onMouseout, _this);
10048         });
10049         this.fireEvent('rowsrendered', this);
10050         
10051         this.autoSize();
10052         
10053         this.initCSS(); /// resize cols
10054
10055         
10056     },
10057     
10058     
10059     onUpdate : function(ds,record)
10060     {
10061         this.refreshRow(record);
10062         this.autoSize();
10063     },
10064     
10065     onRemove : function(ds, record, index, isUpdate){
10066         if(isUpdate !== true){
10067             this.fireEvent("beforerowremoved", this, index, record);
10068         }
10069         var bt = this.bodyEl.dom;
10070         
10071         var rows = this.el.select('tbody > tr', true).elements;
10072         
10073         if(typeof(rows[index]) != 'undefined'){
10074             bt.removeChild(rows[index].dom);
10075         }
10076         
10077 //        if(bt.rows[index]){
10078 //            bt.removeChild(bt.rows[index]);
10079 //        }
10080         
10081         if(isUpdate !== true){
10082             //this.stripeRows(index);
10083             //this.syncRowHeights(index, index);
10084             //this.layout();
10085             this.fireEvent("rowremoved", this, index, record);
10086         }
10087     },
10088     
10089     onAdd : function(ds, records, rowIndex)
10090     {
10091         //Roo.log('on Add called');
10092         // - note this does not handle multiple adding very well..
10093         var bt = this.bodyEl.dom;
10094         for (var i =0 ; i < records.length;i++) {
10095             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10096             //Roo.log(records[i]);
10097             //Roo.log(this.store.getAt(rowIndex+i));
10098             this.insertRow(this.store, rowIndex + i, false);
10099             return;
10100         }
10101         
10102     },
10103     
10104     
10105     refreshRow : function(record){
10106         var ds = this.store, index;
10107         if(typeof record == 'number'){
10108             index = record;
10109             record = ds.getAt(index);
10110         }else{
10111             index = ds.indexOf(record);
10112             if (index < 0) {
10113                 return; // should not happen - but seems to 
10114             }
10115         }
10116         this.insertRow(ds, index, true);
10117         this.autoSize();
10118         this.onRemove(ds, record, index+1, true);
10119         this.autoSize();
10120         //this.syncRowHeights(index, index);
10121         //this.layout();
10122         this.fireEvent("rowupdated", this, index, record);
10123     },
10124     // private - called by RowSelection
10125     onRowSelect : function(rowIndex){
10126         var row = this.getRowDom(rowIndex);
10127         row.addClass(['bg-info','info']);
10128     },
10129     // private - called by RowSelection
10130     onRowDeselect : function(rowIndex)
10131     {
10132         if (rowIndex < 0) {
10133             return;
10134         }
10135         var row = this.getRowDom(rowIndex);
10136         row.removeClass(['bg-info','info']);
10137     },
10138       /**
10139      * Focuses the specified row.
10140      * @param {Number} row The row index
10141      */
10142     focusRow : function(row)
10143     {
10144         //Roo.log('GridView.focusRow');
10145         var x = this.bodyEl.dom.scrollLeft;
10146         this.focusCell(row, 0, false);
10147         this.bodyEl.dom.scrollLeft = x;
10148
10149     },
10150      /**
10151      * Focuses the specified cell.
10152      * @param {Number} row The row index
10153      * @param {Number} col The column index
10154      * @param {Boolean} hscroll false to disable horizontal scrolling
10155      */
10156     focusCell : function(row, col, hscroll)
10157     {
10158         //Roo.log('GridView.focusCell');
10159         var el = this.ensureVisible(row, col, hscroll);
10160         // not sure what focusEL achives = it's a <a> pos relative 
10161         //this.focusEl.alignTo(el, "tl-tl");
10162         //if(Roo.isGecko){
10163         //    this.focusEl.focus();
10164         //}else{
10165         //    this.focusEl.focus.defer(1, this.focusEl);
10166         //}
10167     },
10168     
10169      /**
10170      * Scrolls the specified cell into view
10171      * @param {Number} row The row index
10172      * @param {Number} col The column index
10173      * @param {Boolean} hscroll false to disable horizontal scrolling
10174      */
10175     ensureVisible : function(row, col, hscroll)
10176     {
10177         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10178         //return null; //disable for testing.
10179         if(typeof row != "number"){
10180             row = row.rowIndex;
10181         }
10182         if(row < 0 && row >= this.ds.getCount()){
10183             return  null;
10184         }
10185         col = (col !== undefined ? col : 0);
10186         var cm = this.cm;
10187         while(cm.isHidden(col)){
10188             col++;
10189         }
10190
10191         var el = this.getCellDom(row, col);
10192         if(!el){
10193             return null;
10194         }
10195         var c = this.bodyEl.dom;
10196
10197         var ctop = parseInt(el.offsetTop, 10);
10198         var cleft = parseInt(el.offsetLeft, 10);
10199         var cbot = ctop + el.offsetHeight;
10200         var cright = cleft + el.offsetWidth;
10201
10202         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10203         var ch = 0; //?? header is not withing the area?
10204         var stop = parseInt(c.scrollTop, 10);
10205         var sleft = parseInt(c.scrollLeft, 10);
10206         var sbot = stop + ch;
10207         var sright = sleft + c.clientWidth;
10208         /*
10209         Roo.log('GridView.ensureVisible:' +
10210                 ' ctop:' + ctop +
10211                 ' c.clientHeight:' + c.clientHeight +
10212                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10213                 ' stop:' + stop +
10214                 ' cbot:' + cbot +
10215                 ' sbot:' + sbot +
10216                 ' ch:' + ch  
10217                 );
10218         */
10219         if(ctop < stop){
10220             c.scrollTop = ctop;
10221             //Roo.log("set scrolltop to ctop DISABLE?");
10222         }else if(cbot > sbot){
10223             //Roo.log("set scrolltop to cbot-ch");
10224             c.scrollTop = cbot-ch;
10225         }
10226
10227         if(hscroll !== false){
10228             if(cleft < sleft){
10229                 c.scrollLeft = cleft;
10230             }else if(cright > sright){
10231                 c.scrollLeft = cright-c.clientWidth;
10232             }
10233         }
10234
10235         return el;
10236     },
10237     
10238     
10239     insertRow : function(dm, rowIndex, isUpdate){
10240         
10241         if(!isUpdate){
10242             this.fireEvent("beforerowsinserted", this, rowIndex);
10243         }
10244             //var s = this.getScrollState();
10245         var row = this.renderRow(this.cm, this.store, rowIndex);
10246         // insert before rowIndex..
10247         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10248         
10249         var _this = this;
10250                 
10251         if(row.cellObjects.length){
10252             Roo.each(row.cellObjects, function(r){
10253                 _this.renderCellObject(r);
10254             })
10255         }
10256             
10257         if(!isUpdate){
10258             this.fireEvent("rowsinserted", this, rowIndex);
10259             //this.syncRowHeights(firstRow, lastRow);
10260             //this.stripeRows(firstRow);
10261             //this.layout();
10262         }
10263         
10264     },
10265     
10266     
10267     getRowDom : function(rowIndex)
10268     {
10269         var rows = this.el.select('tbody > tr', true).elements;
10270         
10271         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10272         
10273     },
10274     getCellDom : function(rowIndex, colIndex)
10275     {
10276         var row = this.getRowDom(rowIndex);
10277         if (row === false) {
10278             return false;
10279         }
10280         var cols = row.select('td', true).elements;
10281         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10282         
10283     },
10284     
10285     // returns the object tree for a tr..
10286   
10287     
10288     renderRow : function(cm, ds, rowIndex) 
10289     {
10290         var d = ds.getAt(rowIndex);
10291         
10292         var row = {
10293             tag : 'tr',
10294             cls : 'x-row-' + rowIndex,
10295             cn : []
10296         };
10297             
10298         var cellObjects = [];
10299         
10300         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10301             var config = cm.config[i];
10302             
10303             var renderer = cm.getRenderer(i);
10304             var value = '';
10305             var id = false;
10306             
10307             if(typeof(renderer) !== 'undefined'){
10308                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10309             }
10310             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10311             // and are rendered into the cells after the row is rendered - using the id for the element.
10312             
10313             if(typeof(value) === 'object'){
10314                 id = Roo.id();
10315                 cellObjects.push({
10316                     container : id,
10317                     cfg : value 
10318                 })
10319             }
10320             
10321             var rowcfg = {
10322                 record: d,
10323                 rowIndex : rowIndex,
10324                 colIndex : i,
10325                 rowClass : ''
10326             };
10327
10328             this.fireEvent('rowclass', this, rowcfg);
10329             
10330             var td = {
10331                 tag: 'td',
10332                 // this might end up displaying HTML?
10333                 // this is too messy... - better to only do it on columsn you know are going to be too long
10334                 //tooltip : (typeof(value) === 'object') ? '' : value,
10335                 cls : rowcfg.rowClass + ' x-col-' + i,
10336                 style: '',
10337                 html: (typeof(value) === 'object') ? '' : value
10338             };
10339             
10340             if (id) {
10341                 td.id = id;
10342             }
10343             
10344             if(typeof(config.colspan) != 'undefined'){
10345                 td.colspan = config.colspan;
10346             }
10347             
10348             
10349             
10350             if(typeof(config.align) != 'undefined' && config.align.length){
10351                 td.style += ' text-align:' + config.align + ';';
10352             }
10353             if(typeof(config.valign) != 'undefined' && config.valign.length){
10354                 td.style += ' vertical-align:' + config.valign + ';';
10355             }
10356             /*
10357             if(typeof(config.width) != 'undefined'){
10358                 td.style += ' width:' +  config.width + 'px;';
10359             }
10360             */
10361             
10362             if(typeof(config.cursor) != 'undefined'){
10363                 td.style += ' cursor:' +  config.cursor + ';';
10364             }
10365             
10366             if(typeof(config.cls) != 'undefined'){
10367                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10368             }
10369             if (this.responsive) {
10370                 ['xs','sm','md','lg'].map(function(size){
10371                     
10372                     if(typeof(config[size]) == 'undefined'){
10373                         return;
10374                     }
10375                     
10376                     
10377                       
10378                     if (!config[size]) { // 0 = hidden
10379                         // BS 4 '0' is treated as hide that column and below.
10380                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10381                         return;
10382                     }
10383                     
10384                     td.cls += ' col-' + size + '-' + config[size] + (
10385                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10386                     );
10387                      
10388     
10389                 });
10390             }
10391             row.cn.push(td);
10392            
10393         }
10394         
10395         row.cellObjects = cellObjects;
10396         
10397         return row;
10398           
10399     },
10400     
10401     
10402     
10403     onBeforeLoad : function()
10404     {
10405         this.el.unmask(); // if needed.
10406     },
10407      /**
10408      * Remove all rows
10409      */
10410     clear : function()
10411     {
10412         this.el.select('tbody', true).first().dom.innerHTML = '';
10413     },
10414     /**
10415      * Show or hide a row.
10416      * @param {Number} rowIndex to show or hide
10417      * @param {Boolean} state hide
10418      */
10419     setRowVisibility : function(rowIndex, state)
10420     {
10421         var bt = this.bodyEl.dom;
10422         
10423         var rows = this.el.select('tbody > tr', true).elements;
10424         
10425         if(typeof(rows[rowIndex]) == 'undefined'){
10426             return;
10427         }
10428         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10429         
10430     },
10431     
10432     
10433     getSelectionModel : function(){
10434         if(!this.selModel){
10435             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10436         }
10437         return this.selModel;
10438     },
10439     /*
10440      * Render the Roo.bootstrap object from renderder
10441      */
10442     renderCellObject : function(r)
10443     {
10444         var _this = this;
10445         
10446         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10447         
10448         var t = r.cfg.render(r.container);
10449         
10450         if(r.cfg.cn){
10451             Roo.each(r.cfg.cn, function(c){
10452                 var child = {
10453                     container: t.getChildContainer(),
10454                     cfg: c
10455                 };
10456                 _this.renderCellObject(child);
10457             })
10458         }
10459     },
10460     /**
10461      * get the Row Index from a dom element.
10462      * @param {Roo.Element} row The row to look for
10463      * @returns {Number} the row
10464      */
10465     getRowIndex : function(row)
10466     {
10467         var rowIndex = -1;
10468         
10469         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10470             if(el != row){
10471                 return;
10472             }
10473             
10474             rowIndex = index;
10475         });
10476         
10477         return rowIndex;
10478     },
10479     /**
10480      * get the header TH element for columnIndex
10481      * @param {Number} columnIndex
10482      * @returns {Roo.Element}
10483      */
10484     getHeaderIndex: function(colIndex)
10485     {
10486         var cols = this.headEl.select('th', true).elements;
10487         return cols[colIndex]; 
10488     },
10489     /**
10490      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10491      * @param {domElement} cell to look for
10492      * @returns {Number} the column
10493      */
10494     getCellIndex : function(cell)
10495     {
10496         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10497         if(id){
10498             return parseInt(id[1], 10);
10499         }
10500         return 0;
10501     },
10502      /**
10503      * Returns the grid's underlying element = used by panel.Grid
10504      * @return {Element} The element
10505      */
10506     getGridEl : function(){
10507         return this.el;
10508     },
10509      /**
10510      * Forces a resize - used by panel.Grid
10511      * @return {Element} The element
10512      */
10513     autoSize : function()
10514     {
10515         if(this.disableAutoSize) {
10516             Roo.log("DISABLEAUTOSIZE");
10517             return;
10518         }
10519         //var ctr = Roo.get(this.container.dom.parentElement);
10520         var ctr = Roo.get(this.el.dom);
10521         
10522         var thd = this.getGridEl().select('thead',true).first();
10523         var tbd = this.getGridEl().select('tbody', true).first();
10524         var tfd = this.getGridEl().select('tfoot', true).first();
10525         
10526         var cw = ctr.getWidth();
10527         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10528         
10529         if (tbd) {
10530             
10531             tbd.setWidth(ctr.getWidth());
10532             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10533             // this needs fixing for various usage - currently only hydra job advers I think..
10534             //tdb.setHeight(
10535             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10536             //); 
10537             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10538             cw -= barsize;
10539         }
10540         cw = Math.max(cw, this.totalWidth);
10541         this.getGridEl().select('tbody tr',true).setWidth(cw);
10542         this.initCSS();
10543         
10544         // resize 'expandable coloumn?
10545         
10546         return; // we doe not have a view in this design..
10547         
10548     },
10549     onBodyScroll: function()
10550     {
10551         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10552         if(this.headEl){
10553             this.headEl.setStyle({
10554                 'position' : 'relative',
10555                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10556             });
10557         }
10558         
10559         if(this.lazyLoad){
10560             
10561             var scrollHeight = this.bodyEl.dom.scrollHeight;
10562             
10563             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10564             
10565             var height = this.bodyEl.getHeight();
10566             
10567             if(scrollHeight - height == scrollTop) {
10568                 
10569                 var total = this.ds.getTotalCount();
10570                 
10571                 if(this.footer.cursor + this.footer.pageSize < total){
10572                     
10573                     this.footer.ds.load({
10574                         params : {
10575                             start : this.footer.cursor + this.footer.pageSize,
10576                             limit : this.footer.pageSize
10577                         },
10578                         add : true
10579                     });
10580                 }
10581             }
10582             
10583         }
10584     },
10585     onColumnSplitterMoved : function(i, diff)
10586     {
10587         this.userResized = true;
10588         
10589         var cm = this.colModel;
10590         
10591         var w = this.getHeaderIndex(i).getWidth() + diff;
10592         
10593         
10594         cm.setColumnWidth(i, w, true);
10595         this.initCSS();
10596         //var cid = cm.getColumnId(i); << not used in this version?
10597        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10598         
10599         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10600         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10601         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10602 */
10603         //this.updateSplitters();
10604         //this.layout(); << ??
10605         this.fireEvent("columnresize", i, w);
10606     },
10607     onHeaderChange : function()
10608     {
10609         var header = this.renderHeader();
10610         var table = this.el.select('table', true).first();
10611         
10612         this.headEl.remove();
10613         this.headEl = table.createChild(header, this.bodyEl, false);
10614         
10615         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10616             e.on('click', this.sort, this);
10617         }, this);
10618         
10619         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10620             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10621         }
10622         
10623     },
10624     
10625     onHiddenChange : function(colModel, colIndex, hidden)
10626     {
10627         /*
10628         this.cm.setHidden()
10629         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10630         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10631         
10632         this.CSS.updateRule(thSelector, "display", "");
10633         this.CSS.updateRule(tdSelector, "display", "");
10634         
10635         if(hidden){
10636             this.CSS.updateRule(thSelector, "display", "none");
10637             this.CSS.updateRule(tdSelector, "display", "none");
10638         }
10639         */
10640         // onload calls initCSS()
10641         this.onHeaderChange();
10642         this.onLoad();
10643     },
10644     
10645     setColumnWidth: function(col_index, width)
10646     {
10647         // width = "md-2 xs-2..."
10648         if(!this.colModel.config[col_index]) {
10649             return;
10650         }
10651         
10652         var w = width.split(" ");
10653         
10654         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10655         
10656         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10657         
10658         
10659         for(var j = 0; j < w.length; j++) {
10660             
10661             if(!w[j]) {
10662                 continue;
10663             }
10664             
10665             var size_cls = w[j].split("-");
10666             
10667             if(!Number.isInteger(size_cls[1] * 1)) {
10668                 continue;
10669             }
10670             
10671             if(!this.colModel.config[col_index][size_cls[0]]) {
10672                 continue;
10673             }
10674             
10675             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10676                 continue;
10677             }
10678             
10679             h_row[0].classList.replace(
10680                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10681                 "col-"+size_cls[0]+"-"+size_cls[1]
10682             );
10683             
10684             for(var i = 0; i < rows.length; i++) {
10685                 
10686                 var size_cls = w[j].split("-");
10687                 
10688                 if(!Number.isInteger(size_cls[1] * 1)) {
10689                     continue;
10690                 }
10691                 
10692                 if(!this.colModel.config[col_index][size_cls[0]]) {
10693                     continue;
10694                 }
10695                 
10696                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10697                     continue;
10698                 }
10699                 
10700                 rows[i].classList.replace(
10701                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10702                     "col-"+size_cls[0]+"-"+size_cls[1]
10703                 );
10704             }
10705             
10706             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10707         }
10708     }
10709 });
10710
10711 // currently only used to find the split on drag.. 
10712 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10713
10714 /**
10715  * @depricated
10716 */
10717 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10718 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10719 /*
10720  * - LGPL
10721  *
10722  * table cell
10723  * 
10724  */
10725
10726 /**
10727  * @class Roo.bootstrap.TableCell
10728  * @extends Roo.bootstrap.Component
10729  * @children Roo.bootstrap.Component
10730  * @parent Roo.bootstrap.TableRow
10731  * Bootstrap TableCell class
10732  * 
10733  * @cfg {String} html cell contain text
10734  * @cfg {String} cls cell class
10735  * @cfg {String} tag cell tag (td|th) default td
10736  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10737  * @cfg {String} align Aligns the content in a cell
10738  * @cfg {String} axis Categorizes cells
10739  * @cfg {String} bgcolor Specifies the background color of a cell
10740  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10741  * @cfg {Number} colspan Specifies the number of columns a cell should span
10742  * @cfg {String} headers Specifies one or more header cells a cell is related to
10743  * @cfg {Number} height Sets the height of a cell
10744  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10745  * @cfg {Number} rowspan Sets the number of rows a cell should span
10746  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10747  * @cfg {String} valign Vertical aligns the content in a cell
10748  * @cfg {Number} width Specifies the width of a cell
10749  * 
10750  * @constructor
10751  * Create a new TableCell
10752  * @param {Object} config The config object
10753  */
10754
10755 Roo.bootstrap.TableCell = function(config){
10756     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10757 };
10758
10759 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10760     
10761     html: false,
10762     cls: false,
10763     tag: false,
10764     abbr: false,
10765     align: false,
10766     axis: false,
10767     bgcolor: false,
10768     charoff: false,
10769     colspan: false,
10770     headers: false,
10771     height: false,
10772     nowrap: false,
10773     rowspan: false,
10774     scope: false,
10775     valign: false,
10776     width: false,
10777     
10778     
10779     getAutoCreate : function(){
10780         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10781         
10782         cfg = {
10783             tag: 'td'
10784         };
10785         
10786         if(this.tag){
10787             cfg.tag = this.tag;
10788         }
10789         
10790         if (this.html) {
10791             cfg.html=this.html
10792         }
10793         if (this.cls) {
10794             cfg.cls=this.cls
10795         }
10796         if (this.abbr) {
10797             cfg.abbr=this.abbr
10798         }
10799         if (this.align) {
10800             cfg.align=this.align
10801         }
10802         if (this.axis) {
10803             cfg.axis=this.axis
10804         }
10805         if (this.bgcolor) {
10806             cfg.bgcolor=this.bgcolor
10807         }
10808         if (this.charoff) {
10809             cfg.charoff=this.charoff
10810         }
10811         if (this.colspan) {
10812             cfg.colspan=this.colspan
10813         }
10814         if (this.headers) {
10815             cfg.headers=this.headers
10816         }
10817         if (this.height) {
10818             cfg.height=this.height
10819         }
10820         if (this.nowrap) {
10821             cfg.nowrap=this.nowrap
10822         }
10823         if (this.rowspan) {
10824             cfg.rowspan=this.rowspan
10825         }
10826         if (this.scope) {
10827             cfg.scope=this.scope
10828         }
10829         if (this.valign) {
10830             cfg.valign=this.valign
10831         }
10832         if (this.width) {
10833             cfg.width=this.width
10834         }
10835         
10836         
10837         return cfg;
10838     }
10839    
10840 });
10841
10842  
10843
10844  /*
10845  * - LGPL
10846  *
10847  * table row
10848  * 
10849  */
10850
10851 /**
10852  * @class Roo.bootstrap.TableRow
10853  * @extends Roo.bootstrap.Component
10854  * @children Roo.bootstrap.TableCell
10855  * @parent Roo.bootstrap.TableBody
10856  * Bootstrap TableRow class
10857  * @cfg {String} cls row class
10858  * @cfg {String} align Aligns the content in a table row
10859  * @cfg {String} bgcolor Specifies a background color for a table row
10860  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10861  * @cfg {String} valign Vertical aligns the content in a table row
10862  * 
10863  * @constructor
10864  * Create a new TableRow
10865  * @param {Object} config The config object
10866  */
10867
10868 Roo.bootstrap.TableRow = function(config){
10869     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10870 };
10871
10872 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10873     
10874     cls: false,
10875     align: false,
10876     bgcolor: false,
10877     charoff: false,
10878     valign: false,
10879     
10880     getAutoCreate : function(){
10881         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10882         
10883         cfg = {
10884             tag: 'tr'
10885         };
10886             
10887         if(this.cls){
10888             cfg.cls = this.cls;
10889         }
10890         if(this.align){
10891             cfg.align = this.align;
10892         }
10893         if(this.bgcolor){
10894             cfg.bgcolor = this.bgcolor;
10895         }
10896         if(this.charoff){
10897             cfg.charoff = this.charoff;
10898         }
10899         if(this.valign){
10900             cfg.valign = this.valign;
10901         }
10902         
10903         return cfg;
10904     }
10905    
10906 });
10907
10908  
10909
10910  /*
10911  * - LGPL
10912  *
10913  * table body
10914  * 
10915  */
10916
10917 /**
10918  * @class Roo.bootstrap.TableBody
10919  * @extends Roo.bootstrap.Component
10920  * @children Roo.bootstrap.TableRow
10921  * @parent Roo.bootstrap.Table
10922  * Bootstrap TableBody class
10923  * @cfg {String} cls element class
10924  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10925  * @cfg {String} align Aligns the content inside the element
10926  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10927  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10928  * 
10929  * @constructor
10930  * Create a new TableBody
10931  * @param {Object} config The config object
10932  */
10933
10934 Roo.bootstrap.TableBody = function(config){
10935     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10936 };
10937
10938 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10939     
10940     cls: false,
10941     tag: false,
10942     align: false,
10943     charoff: false,
10944     valign: false,
10945     
10946     getAutoCreate : function(){
10947         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10948         
10949         cfg = {
10950             tag: 'tbody'
10951         };
10952             
10953         if (this.cls) {
10954             cfg.cls=this.cls
10955         }
10956         if(this.tag){
10957             cfg.tag = this.tag;
10958         }
10959         
10960         if(this.align){
10961             cfg.align = this.align;
10962         }
10963         if(this.charoff){
10964             cfg.charoff = this.charoff;
10965         }
10966         if(this.valign){
10967             cfg.valign = this.valign;
10968         }
10969         
10970         return cfg;
10971     }
10972     
10973     
10974 //    initEvents : function()
10975 //    {
10976 //        
10977 //        if(!this.store){
10978 //            return;
10979 //        }
10980 //        
10981 //        this.store = Roo.factory(this.store, Roo.data);
10982 //        this.store.on('load', this.onLoad, this);
10983 //        
10984 //        this.store.load();
10985 //        
10986 //    },
10987 //    
10988 //    onLoad: function () 
10989 //    {   
10990 //        this.fireEvent('load', this);
10991 //    }
10992 //    
10993 //   
10994 });
10995
10996  
10997
10998  /*
10999  * Based on:
11000  * Ext JS Library 1.1.1
11001  * Copyright(c) 2006-2007, Ext JS, LLC.
11002  *
11003  * Originally Released Under LGPL - original licence link has changed is not relivant.
11004  *
11005  * Fork - LGPL
11006  * <script type="text/javascript">
11007  */
11008
11009 // as we use this in bootstrap.
11010 Roo.namespace('Roo.form');
11011  /**
11012  * @class Roo.form.Action
11013  * Internal Class used to handle form actions
11014  * @constructor
11015  * @param {Roo.form.BasicForm} el The form element or its id
11016  * @param {Object} config Configuration options
11017  */
11018
11019  
11020  
11021 // define the action interface
11022 Roo.form.Action = function(form, options){
11023     this.form = form;
11024     this.options = options || {};
11025 };
11026 /**
11027  * Client Validation Failed
11028  * @const 
11029  */
11030 Roo.form.Action.CLIENT_INVALID = 'client';
11031 /**
11032  * Server Validation Failed
11033  * @const 
11034  */
11035 Roo.form.Action.SERVER_INVALID = 'server';
11036  /**
11037  * Connect to Server Failed
11038  * @const 
11039  */
11040 Roo.form.Action.CONNECT_FAILURE = 'connect';
11041 /**
11042  * Reading Data from Server Failed
11043  * @const 
11044  */
11045 Roo.form.Action.LOAD_FAILURE = 'load';
11046
11047 Roo.form.Action.prototype = {
11048     type : 'default',
11049     failureType : undefined,
11050     response : undefined,
11051     result : undefined,
11052
11053     // interface method
11054     run : function(options){
11055
11056     },
11057
11058     // interface method
11059     success : function(response){
11060
11061     },
11062
11063     // interface method
11064     handleResponse : function(response){
11065
11066     },
11067
11068     // default connection failure
11069     failure : function(response){
11070         
11071         this.response = response;
11072         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11073         this.form.afterAction(this, false);
11074     },
11075
11076     processResponse : function(response){
11077         this.response = response;
11078         if(!response.responseText){
11079             return true;
11080         }
11081         this.result = this.handleResponse(response);
11082         return this.result;
11083     },
11084
11085     // utility functions used internally
11086     getUrl : function(appendParams){
11087         var url = this.options.url || this.form.url || this.form.el.dom.action;
11088         if(appendParams){
11089             var p = this.getParams();
11090             if(p){
11091                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11092             }
11093         }
11094         return url;
11095     },
11096
11097     getMethod : function(){
11098         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11099     },
11100
11101     getParams : function(){
11102         var bp = this.form.baseParams;
11103         var p = this.options.params;
11104         if(p){
11105             if(typeof p == "object"){
11106                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11107             }else if(typeof p == 'string' && bp){
11108                 p += '&' + Roo.urlEncode(bp);
11109             }
11110         }else if(bp){
11111             p = Roo.urlEncode(bp);
11112         }
11113         return p;
11114     },
11115
11116     createCallback : function(){
11117         return {
11118             success: this.success,
11119             failure: this.failure,
11120             scope: this,
11121             timeout: (this.form.timeout*1000),
11122             upload: this.form.fileUpload ? this.success : undefined
11123         };
11124     }
11125 };
11126
11127 Roo.form.Action.Submit = function(form, options){
11128     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11129 };
11130
11131 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11132     type : 'submit',
11133
11134     haveProgress : false,
11135     uploadComplete : false,
11136     
11137     // uploadProgress indicator.
11138     uploadProgress : function()
11139     {
11140         if (!this.form.progressUrl) {
11141             return;
11142         }
11143         
11144         if (!this.haveProgress) {
11145             Roo.MessageBox.progress("Uploading", "Uploading");
11146         }
11147         if (this.uploadComplete) {
11148            Roo.MessageBox.hide();
11149            return;
11150         }
11151         
11152         this.haveProgress = true;
11153    
11154         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11155         
11156         var c = new Roo.data.Connection();
11157         c.request({
11158             url : this.form.progressUrl,
11159             params: {
11160                 id : uid
11161             },
11162             method: 'GET',
11163             success : function(req){
11164                //console.log(data);
11165                 var rdata = false;
11166                 var edata;
11167                 try  {
11168                    rdata = Roo.decode(req.responseText)
11169                 } catch (e) {
11170                     Roo.log("Invalid data from server..");
11171                     Roo.log(edata);
11172                     return;
11173                 }
11174                 if (!rdata || !rdata.success) {
11175                     Roo.log(rdata);
11176                     Roo.MessageBox.alert(Roo.encode(rdata));
11177                     return;
11178                 }
11179                 var data = rdata.data;
11180                 
11181                 if (this.uploadComplete) {
11182                    Roo.MessageBox.hide();
11183                    return;
11184                 }
11185                    
11186                 if (data){
11187                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11188                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11189                     );
11190                 }
11191                 this.uploadProgress.defer(2000,this);
11192             },
11193        
11194             failure: function(data) {
11195                 Roo.log('progress url failed ');
11196                 Roo.log(data);
11197             },
11198             scope : this
11199         });
11200            
11201     },
11202     
11203     
11204     run : function()
11205     {
11206         // run get Values on the form, so it syncs any secondary forms.
11207         this.form.getValues();
11208         
11209         var o = this.options;
11210         var method = this.getMethod();
11211         var isPost = method == 'POST';
11212         if(o.clientValidation === false || this.form.isValid()){
11213             
11214             if (this.form.progressUrl) {
11215                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11216                     (new Date() * 1) + '' + Math.random());
11217                     
11218             } 
11219             
11220             
11221             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11222                 form:this.form.el.dom,
11223                 url:this.getUrl(!isPost),
11224                 method: method,
11225                 params:isPost ? this.getParams() : null,
11226                 isUpload: this.form.fileUpload,
11227                 formData : this.form.formData
11228             }));
11229             
11230             this.uploadProgress();
11231
11232         }else if (o.clientValidation !== false){ // client validation failed
11233             this.failureType = Roo.form.Action.CLIENT_INVALID;
11234             this.form.afterAction(this, false);
11235         }
11236     },
11237
11238     success : function(response)
11239     {
11240         this.uploadComplete= true;
11241         if (this.haveProgress) {
11242             Roo.MessageBox.hide();
11243         }
11244         
11245         
11246         var result = this.processResponse(response);
11247         if(result === true || result.success){
11248             this.form.afterAction(this, true);
11249             return;
11250         }
11251         if(result.errors){
11252             this.form.markInvalid(result.errors);
11253             this.failureType = Roo.form.Action.SERVER_INVALID;
11254         }
11255         this.form.afterAction(this, false);
11256     },
11257     failure : function(response)
11258     {
11259         this.uploadComplete= true;
11260         if (this.haveProgress) {
11261             Roo.MessageBox.hide();
11262         }
11263         
11264         this.response = response;
11265         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11266         this.form.afterAction(this, false);
11267     },
11268     
11269     handleResponse : function(response){
11270         if(this.form.errorReader){
11271             var rs = this.form.errorReader.read(response);
11272             var errors = [];
11273             if(rs.records){
11274                 for(var i = 0, len = rs.records.length; i < len; i++) {
11275                     var r = rs.records[i];
11276                     errors[i] = r.data;
11277                 }
11278             }
11279             if(errors.length < 1){
11280                 errors = null;
11281             }
11282             return {
11283                 success : rs.success,
11284                 errors : errors
11285             };
11286         }
11287         var ret = false;
11288         try {
11289             ret = Roo.decode(response.responseText);
11290         } catch (e) {
11291             ret = {
11292                 success: false,
11293                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11294                 errors : []
11295             };
11296         }
11297         return ret;
11298         
11299     }
11300 });
11301
11302
11303 Roo.form.Action.Load = function(form, options){
11304     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11305     this.reader = this.form.reader;
11306 };
11307
11308 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11309     type : 'load',
11310
11311     run : function(){
11312         
11313         Roo.Ajax.request(Roo.apply(
11314                 this.createCallback(), {
11315                     method:this.getMethod(),
11316                     url:this.getUrl(false),
11317                     params:this.getParams()
11318         }));
11319     },
11320
11321     success : function(response){
11322         
11323         var result = this.processResponse(response);
11324         if(result === true || !result.success || !result.data){
11325             this.failureType = Roo.form.Action.LOAD_FAILURE;
11326             this.form.afterAction(this, false);
11327             return;
11328         }
11329         this.form.clearInvalid();
11330         this.form.setValues(result.data);
11331         this.form.afterAction(this, true);
11332     },
11333
11334     handleResponse : function(response){
11335         if(this.form.reader){
11336             var rs = this.form.reader.read(response);
11337             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11338             return {
11339                 success : rs.success,
11340                 data : data
11341             };
11342         }
11343         return Roo.decode(response.responseText);
11344     }
11345 });
11346
11347 Roo.form.Action.ACTION_TYPES = {
11348     'load' : Roo.form.Action.Load,
11349     'submit' : Roo.form.Action.Submit
11350 };/*
11351  * - LGPL
11352  *
11353  * form
11354  *
11355  */
11356
11357 /**
11358  * @class Roo.bootstrap.form.Form
11359  * @extends Roo.bootstrap.Component
11360  * @children Roo.bootstrap.Component
11361  * Bootstrap Form class
11362  * @cfg {String} method  GET | POST (default POST)
11363  * @cfg {String} labelAlign top | left (default top)
11364  * @cfg {String} align left  | right - for navbars
11365  * @cfg {Boolean} loadMask load mask when submit (default true)
11366
11367  *
11368  * @constructor
11369  * Create a new Form
11370  * @param {Object} config The config object
11371  */
11372
11373
11374 Roo.bootstrap.form.Form = function(config){
11375     
11376     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11377     
11378     Roo.bootstrap.form.Form.popover.apply();
11379     
11380     this.addEvents({
11381         /**
11382          * @event clientvalidation
11383          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11384          * @param {Form} this
11385          * @param {Boolean} valid true if the form has passed client-side validation
11386          */
11387         clientvalidation: true,
11388         /**
11389          * @event beforeaction
11390          * Fires before any action is performed. Return false to cancel the action.
11391          * @param {Form} this
11392          * @param {Action} action The action to be performed
11393          */
11394         beforeaction: true,
11395         /**
11396          * @event actionfailed
11397          * Fires when an action fails.
11398          * @param {Form} this
11399          * @param {Action} action The action that failed
11400          */
11401         actionfailed : true,
11402         /**
11403          * @event actioncomplete
11404          * Fires when an action is completed.
11405          * @param {Form} this
11406          * @param {Action} action The action that completed
11407          */
11408         actioncomplete : true
11409     });
11410 };
11411
11412 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11413
11414      /**
11415      * @cfg {String} method
11416      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11417      */
11418     method : 'POST',
11419     /**
11420      * @cfg {String} url
11421      * The URL to use for form actions if one isn't supplied in the action options.
11422      */
11423     /**
11424      * @cfg {Boolean} fileUpload
11425      * Set to true if this form is a file upload.
11426      */
11427
11428     /**
11429      * @cfg {Object} baseParams
11430      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11431      */
11432
11433     /**
11434      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11435      */
11436     timeout: 30,
11437     /**
11438      * @cfg {Sting} align (left|right) for navbar forms
11439      */
11440     align : 'left',
11441
11442     // private
11443     activeAction : null,
11444
11445     /**
11446      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11447      * element by passing it or its id or mask the form itself by passing in true.
11448      * @type Mixed
11449      */
11450     waitMsgTarget : false,
11451
11452     loadMask : true,
11453     
11454     /**
11455      * @cfg {Boolean} errorMask (true|false) default false
11456      */
11457     errorMask : false,
11458     
11459     /**
11460      * @cfg {Number} maskOffset Default 100
11461      */
11462     maskOffset : 100,
11463     
11464     /**
11465      * @cfg {Boolean} maskBody
11466      */
11467     maskBody : false,
11468
11469     getAutoCreate : function(){
11470
11471         var cfg = {
11472             tag: 'form',
11473             method : this.method || 'POST',
11474             id : this.id || Roo.id(),
11475             cls : ''
11476         };
11477         if (this.parent().xtype.match(/^Nav/)) {
11478             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11479
11480         }
11481
11482         if (this.labelAlign == 'left' ) {
11483             cfg.cls += ' form-horizontal';
11484         }
11485
11486
11487         return cfg;
11488     },
11489     initEvents : function()
11490     {
11491         this.el.on('submit', this.onSubmit, this);
11492         // this was added as random key presses on the form where triggering form submit.
11493         this.el.on('keypress', function(e) {
11494             if (e.getCharCode() != 13) {
11495                 return true;
11496             }
11497             // we might need to allow it for textareas.. and some other items.
11498             // check e.getTarget().
11499
11500             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11501                 return true;
11502             }
11503
11504             Roo.log("keypress blocked");
11505
11506             e.preventDefault();
11507             return false;
11508         });
11509         
11510     },
11511     // private
11512     onSubmit : function(e){
11513         e.stopEvent();
11514     },
11515
11516      /**
11517      * Returns true if client-side validation on the form is successful.
11518      * @return Boolean
11519      */
11520     isValid : function(){
11521         var items = this.getItems();
11522         var valid = true;
11523         var target = false;
11524         
11525         items.each(function(f){
11526             
11527             if(f.validate()){
11528                 return;
11529             }
11530             
11531             Roo.log('invalid field: ' + f.name);
11532             
11533             valid = false;
11534
11535             if(!target && f.el.isVisible(true)){
11536                 target = f;
11537             }
11538            
11539         });
11540         
11541         if(this.errorMask && !valid){
11542             Roo.bootstrap.form.Form.popover.mask(this, target);
11543         }
11544         
11545         return valid;
11546     },
11547     
11548     /**
11549      * Returns true if any fields in this form have changed since their original load.
11550      * @return Boolean
11551      */
11552     isDirty : function(){
11553         var dirty = false;
11554         var items = this.getItems();
11555         items.each(function(f){
11556            if(f.isDirty()){
11557                dirty = true;
11558                return false;
11559            }
11560            return true;
11561         });
11562         return dirty;
11563     },
11564      /**
11565      * Performs a predefined action (submit or load) or custom actions you define on this form.
11566      * @param {String} actionName The name of the action type
11567      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11568      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11569      * accept other config options):
11570      * <pre>
11571 Property          Type             Description
11572 ----------------  ---------------  ----------------------------------------------------------------------------------
11573 url               String           The url for the action (defaults to the form's url)
11574 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11575 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11576 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11577                                    validate the form on the client (defaults to false)
11578      * </pre>
11579      * @return {BasicForm} this
11580      */
11581     doAction : function(action, options){
11582         if(typeof action == 'string'){
11583             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11584         }
11585         if(this.fireEvent('beforeaction', this, action) !== false){
11586             this.beforeAction(action);
11587             action.run.defer(100, action);
11588         }
11589         return this;
11590     },
11591
11592     // private
11593     beforeAction : function(action){
11594         var o = action.options;
11595         
11596         if(this.loadMask){
11597             
11598             if(this.maskBody){
11599                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11600             } else {
11601                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11602             }
11603         }
11604         // not really supported yet.. ??
11605
11606         //if(this.waitMsgTarget === true){
11607         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11608         //}else if(this.waitMsgTarget){
11609         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11610         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11611         //}else {
11612         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11613        // }
11614
11615     },
11616
11617     // private
11618     afterAction : function(action, success){
11619         this.activeAction = null;
11620         var o = action.options;
11621
11622         if(this.loadMask){
11623             
11624             if(this.maskBody){
11625                 Roo.get(document.body).unmask();
11626             } else {
11627                 this.el.unmask();
11628             }
11629         }
11630         
11631         //if(this.waitMsgTarget === true){
11632 //            this.el.unmask();
11633         //}else if(this.waitMsgTarget){
11634         //    this.waitMsgTarget.unmask();
11635         //}else{
11636         //    Roo.MessageBox.updateProgress(1);
11637         //    Roo.MessageBox.hide();
11638        // }
11639         //
11640         if(success){
11641             if(o.reset){
11642                 this.reset();
11643             }
11644             Roo.callback(o.success, o.scope, [this, action]);
11645             this.fireEvent('actioncomplete', this, action);
11646
11647         }else{
11648
11649             // failure condition..
11650             // we have a scenario where updates need confirming.
11651             // eg. if a locking scenario exists..
11652             // we look for { errors : { needs_confirm : true }} in the response.
11653             if (
11654                 (typeof(action.result) != 'undefined')  &&
11655                 (typeof(action.result.errors) != 'undefined')  &&
11656                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11657            ){
11658                 var _t = this;
11659                 Roo.log("not supported yet");
11660                  /*
11661
11662                 Roo.MessageBox.confirm(
11663                     "Change requires confirmation",
11664                     action.result.errorMsg,
11665                     function(r) {
11666                         if (r != 'yes') {
11667                             return;
11668                         }
11669                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11670                     }
11671
11672                 );
11673                 */
11674
11675
11676                 return;
11677             }
11678
11679             Roo.callback(o.failure, o.scope, [this, action]);
11680             // show an error message if no failed handler is set..
11681             if (!this.hasListener('actionfailed')) {
11682                 Roo.log("need to add dialog support");
11683                 /*
11684                 Roo.MessageBox.alert("Error",
11685                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11686                         action.result.errorMsg :
11687                         "Saving Failed, please check your entries or try again"
11688                 );
11689                 */
11690             }
11691
11692             this.fireEvent('actionfailed', this, action);
11693         }
11694
11695     },
11696     /**
11697      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11698      * @param {String} id The value to search for
11699      * @return Field
11700      */
11701     findField : function(id){
11702         var items = this.getItems();
11703         var field = items.get(id);
11704         if(!field){
11705              items.each(function(f){
11706                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11707                     field = f;
11708                     return false;
11709                 }
11710                 return true;
11711             });
11712         }
11713         return field || null;
11714     },
11715      /**
11716      * Mark fields in this form invalid in bulk.
11717      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11718      * @return {BasicForm} this
11719      */
11720     markInvalid : function(errors){
11721         if(errors instanceof Array){
11722             for(var i = 0, len = errors.length; i < len; i++){
11723                 var fieldError = errors[i];
11724                 var f = this.findField(fieldError.id);
11725                 if(f){
11726                     f.markInvalid(fieldError.msg);
11727                 }
11728             }
11729         }else{
11730             var field, id;
11731             for(id in errors){
11732                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11733                     field.markInvalid(errors[id]);
11734                 }
11735             }
11736         }
11737         //Roo.each(this.childForms || [], function (f) {
11738         //    f.markInvalid(errors);
11739         //});
11740
11741         return this;
11742     },
11743
11744     /**
11745      * Set values for fields in this form in bulk.
11746      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11747      * @return {BasicForm} this
11748      */
11749     setValues : function(values){
11750         if(values instanceof Array){ // array of objects
11751             for(var i = 0, len = values.length; i < len; i++){
11752                 var v = values[i];
11753                 var f = this.findField(v.id);
11754                 if(f){
11755                     f.setValue(v.value);
11756                     if(this.trackResetOnLoad){
11757                         f.originalValue = f.getValue();
11758                     }
11759                 }
11760             }
11761         }else{ // object hash
11762             var field, id;
11763             for(id in values){
11764                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11765
11766                     if (field.setFromData &&
11767                         field.valueField &&
11768                         field.displayField &&
11769                         // combos' with local stores can
11770                         // be queried via setValue()
11771                         // to set their value..
11772                         (field.store && !field.store.isLocal)
11773                         ) {
11774                         // it's a combo
11775                         var sd = { };
11776                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11777                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11778                         field.setFromData(sd);
11779
11780                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11781                         
11782                         field.setFromData(values);
11783                         
11784                     } else {
11785                         field.setValue(values[id]);
11786                     }
11787
11788
11789                     if(this.trackResetOnLoad){
11790                         field.originalValue = field.getValue();
11791                     }
11792                 }
11793             }
11794         }
11795
11796         //Roo.each(this.childForms || [], function (f) {
11797         //    f.setValues(values);
11798         //});
11799
11800         return this;
11801     },
11802
11803     /**
11804      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11805      * they are returned as an array.
11806      * @param {Boolean} asString
11807      * @return {Object}
11808      */
11809     getValues : function(asString){
11810         //if (this.childForms) {
11811             // copy values from the child forms
11812         //    Roo.each(this.childForms, function (f) {
11813         //        this.setValues(f.getValues());
11814         //    }, this);
11815         //}
11816
11817
11818
11819         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11820         if(asString === true){
11821             return fs;
11822         }
11823         return Roo.urlDecode(fs);
11824     },
11825
11826     /**
11827      * Returns the fields in this form as an object with key/value pairs.
11828      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11829      * @return {Object}
11830      */
11831     getFieldValues : function(with_hidden)
11832     {
11833         var items = this.getItems();
11834         var ret = {};
11835         items.each(function(f){
11836             
11837             if (!f.getName()) {
11838                 return;
11839             }
11840             
11841             var v = f.getValue();
11842             
11843             if (f.inputType =='radio') {
11844                 if (typeof(ret[f.getName()]) == 'undefined') {
11845                     ret[f.getName()] = ''; // empty..
11846                 }
11847
11848                 if (!f.el.dom.checked) {
11849                     return;
11850
11851                 }
11852                 v = f.el.dom.value;
11853
11854             }
11855             
11856             if(f.xtype == 'MoneyField'){
11857                 ret[f.currencyName] = f.getCurrency();
11858             }
11859
11860             // not sure if this supported any more..
11861             if ((typeof(v) == 'object') && f.getRawValue) {
11862                 v = f.getRawValue() ; // dates..
11863             }
11864             // combo boxes where name != hiddenName...
11865             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11866                 ret[f.name] = f.getRawValue();
11867             }
11868             ret[f.getName()] = v;
11869         });
11870
11871         return ret;
11872     },
11873
11874     /**
11875      * Clears all invalid messages in this form.
11876      * @return {BasicForm} this
11877      */
11878     clearInvalid : function(){
11879         var items = this.getItems();
11880
11881         items.each(function(f){
11882            f.clearInvalid();
11883         });
11884
11885         return this;
11886     },
11887
11888     /**
11889      * Resets this form.
11890      * @return {BasicForm} this
11891      */
11892     reset : function(){
11893         var items = this.getItems();
11894         items.each(function(f){
11895             f.reset();
11896         });
11897
11898         Roo.each(this.childForms || [], function (f) {
11899             f.reset();
11900         });
11901
11902
11903         return this;
11904     },
11905     
11906     getItems : function()
11907     {
11908         var r=new Roo.util.MixedCollection(false, function(o){
11909             return o.id || (o.id = Roo.id());
11910         });
11911         var iter = function(el) {
11912             if (el.inputEl) {
11913                 r.add(el);
11914             }
11915             if (!el.items) {
11916                 return;
11917             }
11918             Roo.each(el.items,function(e) {
11919                 iter(e);
11920             });
11921         };
11922
11923         iter(this);
11924         return r;
11925     },
11926     
11927     hideFields : function(items)
11928     {
11929         Roo.each(items, function(i){
11930             
11931             var f = this.findField(i);
11932             
11933             if(!f){
11934                 return;
11935             }
11936             
11937             f.hide();
11938             
11939         }, this);
11940     },
11941     
11942     showFields : function(items)
11943     {
11944         Roo.each(items, function(i){
11945             
11946             var f = this.findField(i);
11947             
11948             if(!f){
11949                 return;
11950             }
11951             
11952             f.show();
11953             
11954         }, this);
11955     }
11956
11957 });
11958
11959 Roo.apply(Roo.bootstrap.form.Form, {
11960     
11961     popover : {
11962         
11963         padding : 5,
11964         
11965         isApplied : false,
11966         
11967         isMasked : false,
11968         
11969         form : false,
11970         
11971         target : false,
11972         
11973         toolTip : false,
11974         
11975         intervalID : false,
11976         
11977         maskEl : false,
11978         
11979         apply : function()
11980         {
11981             if(this.isApplied){
11982                 return;
11983             }
11984             
11985             this.maskEl = {
11986                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11987                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11988                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11989                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11990             };
11991             
11992             this.maskEl.top.enableDisplayMode("block");
11993             this.maskEl.left.enableDisplayMode("block");
11994             this.maskEl.bottom.enableDisplayMode("block");
11995             this.maskEl.right.enableDisplayMode("block");
11996             
11997             this.toolTip = new Roo.bootstrap.Tooltip({
11998                 cls : 'roo-form-error-popover',
11999                 alignment : {
12000                     'left' : ['r-l', [-2,0], 'right'],
12001                     'right' : ['l-r', [2,0], 'left'],
12002                     'bottom' : ['tl-bl', [0,2], 'top'],
12003                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12004                 }
12005             });
12006             
12007             this.toolTip.render(Roo.get(document.body));
12008
12009             this.toolTip.el.enableDisplayMode("block");
12010             
12011             Roo.get(document.body).on('click', function(){
12012                 this.unmask();
12013             }, this);
12014             
12015             Roo.get(document.body).on('touchstart', function(){
12016                 this.unmask();
12017             }, this);
12018             
12019             this.isApplied = true
12020         },
12021         
12022         mask : function(form, target)
12023         {
12024             this.form = form;
12025             
12026             this.target = target;
12027             
12028             if(!this.form.errorMask || !target.el){
12029                 return;
12030             }
12031             
12032             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12033             
12034             Roo.log(scrollable);
12035             
12036             var ot = this.target.el.calcOffsetsTo(scrollable);
12037             
12038             var scrollTo = ot[1] - this.form.maskOffset;
12039             
12040             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12041             
12042             scrollable.scrollTo('top', scrollTo);
12043             
12044             var box = this.target.el.getBox();
12045             Roo.log(box);
12046             var zIndex = Roo.bootstrap.Modal.zIndex++;
12047
12048             
12049             this.maskEl.top.setStyle('position', 'absolute');
12050             this.maskEl.top.setStyle('z-index', zIndex);
12051             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12052             this.maskEl.top.setLeft(0);
12053             this.maskEl.top.setTop(0);
12054             this.maskEl.top.show();
12055             
12056             this.maskEl.left.setStyle('position', 'absolute');
12057             this.maskEl.left.setStyle('z-index', zIndex);
12058             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12059             this.maskEl.left.setLeft(0);
12060             this.maskEl.left.setTop(box.y - this.padding);
12061             this.maskEl.left.show();
12062
12063             this.maskEl.bottom.setStyle('position', 'absolute');
12064             this.maskEl.bottom.setStyle('z-index', zIndex);
12065             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12066             this.maskEl.bottom.setLeft(0);
12067             this.maskEl.bottom.setTop(box.bottom + this.padding);
12068             this.maskEl.bottom.show();
12069
12070             this.maskEl.right.setStyle('position', 'absolute');
12071             this.maskEl.right.setStyle('z-index', zIndex);
12072             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12073             this.maskEl.right.setLeft(box.right + this.padding);
12074             this.maskEl.right.setTop(box.y - this.padding);
12075             this.maskEl.right.show();
12076
12077             this.toolTip.bindEl = this.target.el;
12078
12079             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12080
12081             var tip = this.target.blankText;
12082
12083             if(this.target.getValue() !== '' ) {
12084                 
12085                 if (this.target.invalidText.length) {
12086                     tip = this.target.invalidText;
12087                 } else if (this.target.regexText.length){
12088                     tip = this.target.regexText;
12089                 }
12090             }
12091
12092             this.toolTip.show(tip);
12093
12094             this.intervalID = window.setInterval(function() {
12095                 Roo.bootstrap.form.Form.popover.unmask();
12096             }, 10000);
12097
12098             window.onwheel = function(){ return false;};
12099             
12100             (function(){ this.isMasked = true; }).defer(500, this);
12101             
12102         },
12103         
12104         unmask : function()
12105         {
12106             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12107                 return;
12108             }
12109             
12110             this.maskEl.top.setStyle('position', 'absolute');
12111             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12112             this.maskEl.top.hide();
12113
12114             this.maskEl.left.setStyle('position', 'absolute');
12115             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12116             this.maskEl.left.hide();
12117
12118             this.maskEl.bottom.setStyle('position', 'absolute');
12119             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12120             this.maskEl.bottom.hide();
12121
12122             this.maskEl.right.setStyle('position', 'absolute');
12123             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12124             this.maskEl.right.hide();
12125             
12126             this.toolTip.hide();
12127             
12128             this.toolTip.el.hide();
12129             
12130             window.onwheel = function(){ return true;};
12131             
12132             if(this.intervalID){
12133                 window.clearInterval(this.intervalID);
12134                 this.intervalID = false;
12135             }
12136             
12137             this.isMasked = false;
12138             
12139         }
12140         
12141     }
12142     
12143 });
12144
12145 /*
12146  * Based on:
12147  * Ext JS Library 1.1.1
12148  * Copyright(c) 2006-2007, Ext JS, LLC.
12149  *
12150  * Originally Released Under LGPL - original licence link has changed is not relivant.
12151  *
12152  * Fork - LGPL
12153  * <script type="text/javascript">
12154  */
12155 /**
12156  * @class Roo.form.VTypes
12157  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12158  * @static
12159  */
12160 Roo.form.VTypes = function(){
12161     // closure these in so they are only created once.
12162     var alpha = /^[a-zA-Z_]+$/;
12163     var alphanum = /^[a-zA-Z0-9_]+$/;
12164     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12165     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12166
12167     // All these messages and functions are configurable
12168     return {
12169         /**
12170          * The function used to validate email addresses
12171          * @param {String} value The email address
12172          */
12173         'email' : function(v){
12174             return email.test(v);
12175         },
12176         /**
12177          * The error text to display when the email validation function returns false
12178          * @type String
12179          */
12180         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12181         /**
12182          * The keystroke filter mask to be applied on email input
12183          * @type RegExp
12184          */
12185         'emailMask' : /[a-z0-9_\.\-@]/i,
12186
12187         /**
12188          * The function used to validate URLs
12189          * @param {String} value The URL
12190          */
12191         'url' : function(v){
12192             return url.test(v);
12193         },
12194         /**
12195          * The error text to display when the url validation function returns false
12196          * @type String
12197          */
12198         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12199         
12200         /**
12201          * The function used to validate alpha values
12202          * @param {String} value The value
12203          */
12204         'alpha' : function(v){
12205             return alpha.test(v);
12206         },
12207         /**
12208          * The error text to display when the alpha validation function returns false
12209          * @type String
12210          */
12211         'alphaText' : 'This field should only contain letters and _',
12212         /**
12213          * The keystroke filter mask to be applied on alpha input
12214          * @type RegExp
12215          */
12216         'alphaMask' : /[a-z_]/i,
12217
12218         /**
12219          * The function used to validate alphanumeric values
12220          * @param {String} value The value
12221          */
12222         'alphanum' : function(v){
12223             return alphanum.test(v);
12224         },
12225         /**
12226          * The error text to display when the alphanumeric validation function returns false
12227          * @type String
12228          */
12229         'alphanumText' : 'This field should only contain letters, numbers and _',
12230         /**
12231          * The keystroke filter mask to be applied on alphanumeric input
12232          * @type RegExp
12233          */
12234         'alphanumMask' : /[a-z0-9_]/i
12235     };
12236 }();/*
12237  * - LGPL
12238  *
12239  * Input
12240  * 
12241  */
12242
12243 /**
12244  * @class Roo.bootstrap.form.Input
12245  * @extends Roo.bootstrap.Component
12246  * Bootstrap Input class
12247  * @cfg {Boolean} disabled is it disabled
12248  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12249  * @cfg {String} name name of the input
12250  * @cfg {string} fieldLabel - the label associated
12251  * @cfg {string} placeholder - placeholder to put in text.
12252  * @cfg {string} before - input group add on before
12253  * @cfg {string} after - input group add on after
12254  * @cfg {string} size - (lg|sm) or leave empty..
12255  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12256  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12257  * @cfg {Number} md colspan out of 12 for computer-sized screens
12258  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12259  * @cfg {string} value default value of the input
12260  * @cfg {Number} labelWidth set the width of label 
12261  * @cfg {Number} labellg set the width of label (1-12)
12262  * @cfg {Number} labelmd set the width of label (1-12)
12263  * @cfg {Number} labelsm set the width of label (1-12)
12264  * @cfg {Number} labelxs set the width of label (1-12)
12265  * @cfg {String} labelAlign (top|left)
12266  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12267  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12268  * @cfg {String} indicatorpos (left|right) default left
12269  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12270  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12271  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12272  * @cfg {Roo.bootstrap.Button} before Button to show before
12273  * @cfg {Roo.bootstrap.Button} afterButton to show before
12274  * @cfg {String} align (left|center|right) Default left
12275  * @cfg {Boolean} forceFeedback (true|false) Default false
12276  * 
12277  * @constructor
12278  * Create a new Input
12279  * @param {Object} config The config object
12280  */
12281
12282 Roo.bootstrap.form.Input = function(config){
12283     
12284     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12285     
12286     this.addEvents({
12287         /**
12288          * @event focus
12289          * Fires when this field receives input focus.
12290          * @param {Roo.form.Field} this
12291          */
12292         focus : true,
12293         /**
12294          * @event blur
12295          * Fires when this field loses input focus.
12296          * @param {Roo.form.Field} this
12297          */
12298         blur : true,
12299         /**
12300          * @event specialkey
12301          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12302          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12303          * @param {Roo.form.Field} this
12304          * @param {Roo.EventObject} e The event object
12305          */
12306         specialkey : true,
12307         /**
12308          * @event change
12309          * Fires just before the field blurs if the field value has changed.
12310          * @param {Roo.form.Field} this
12311          * @param {Mixed} newValue The new value
12312          * @param {Mixed} oldValue The original value
12313          */
12314         change : true,
12315         /**
12316          * @event invalid
12317          * Fires after the field has been marked as invalid.
12318          * @param {Roo.form.Field} this
12319          * @param {String} msg The validation message
12320          */
12321         invalid : true,
12322         /**
12323          * @event valid
12324          * Fires after the field has been validated with no errors.
12325          * @param {Roo.form.Field} this
12326          */
12327         valid : true,
12328          /**
12329          * @event keyup
12330          * Fires after the key up
12331          * @param {Roo.form.Field} this
12332          * @param {Roo.EventObject}  e The event Object
12333          */
12334         keyup : true,
12335         /**
12336          * @event paste
12337          * Fires after the user pastes into input
12338          * @param {Roo.form.Field} this
12339          * @param {Roo.EventObject}  e The event Object
12340          */
12341         paste : true
12342     });
12343 };
12344
12345 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12346      /**
12347      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12348       automatic validation (defaults to "keyup").
12349      */
12350     validationEvent : "keyup",
12351      /**
12352      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12353      */
12354     validateOnBlur : true,
12355     /**
12356      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12357      */
12358     validationDelay : 250,
12359      /**
12360      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12361      */
12362     focusClass : "x-form-focus",  // not needed???
12363     
12364        
12365     /**
12366      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12367      */
12368     invalidClass : "has-warning",
12369     
12370     /**
12371      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12372      */
12373     validClass : "has-success",
12374     
12375     /**
12376      * @cfg {Boolean} hasFeedback (true|false) default true
12377      */
12378     hasFeedback : true,
12379     
12380     /**
12381      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12382      */
12383     invalidFeedbackClass : "glyphicon-warning-sign",
12384     
12385     /**
12386      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12387      */
12388     validFeedbackClass : "glyphicon-ok",
12389     
12390     /**
12391      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12392      */
12393     selectOnFocus : false,
12394     
12395      /**
12396      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12397      */
12398     maskRe : null,
12399        /**
12400      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12401      */
12402     vtype : null,
12403     
12404       /**
12405      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12406      */
12407     disableKeyFilter : false,
12408     
12409        /**
12410      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12411      */
12412     disabled : false,
12413      /**
12414      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12415      */
12416     allowBlank : true,
12417     /**
12418      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12419      */
12420     blankText : "Please complete this mandatory field",
12421     
12422      /**
12423      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12424      */
12425     minLength : 0,
12426     /**
12427      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12428      */
12429     maxLength : Number.MAX_VALUE,
12430     /**
12431      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12432      */
12433     minLengthText : "The minimum length for this field is {0}",
12434     /**
12435      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12436      */
12437     maxLengthText : "The maximum length for this field is {0}",
12438   
12439     
12440     /**
12441      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12442      * If available, this function will be called only after the basic validators all return true, and will be passed the
12443      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12444      */
12445     validator : null,
12446     /**
12447      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12448      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12449      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12450      */
12451     regex : null,
12452     /**
12453      * @cfg {String} regexText -- Depricated - use Invalid Text
12454      */
12455     regexText : "",
12456     
12457     /**
12458      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12459      */
12460     invalidText : "",
12461     
12462     
12463     
12464     autocomplete: false,
12465     
12466     
12467     fieldLabel : '',
12468     inputType : 'text',
12469     
12470     name : false,
12471     placeholder: false,
12472     before : false,
12473     after : false,
12474     size : false,
12475     hasFocus : false,
12476     preventMark: false,
12477     isFormField : true,
12478     value : '',
12479     labelWidth : 2,
12480     labelAlign : false,
12481     readOnly : false,
12482     align : false,
12483     formatedValue : false,
12484     forceFeedback : false,
12485     
12486     indicatorpos : 'left',
12487     
12488     labellg : 0,
12489     labelmd : 0,
12490     labelsm : 0,
12491     labelxs : 0,
12492     
12493     capture : '',
12494     accept : '',
12495     
12496     parentLabelAlign : function()
12497     {
12498         var parent = this;
12499         while (parent.parent()) {
12500             parent = parent.parent();
12501             if (typeof(parent.labelAlign) !='undefined') {
12502                 return parent.labelAlign;
12503             }
12504         }
12505         return 'left';
12506         
12507     },
12508     
12509     getAutoCreate : function()
12510     {
12511         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12512         
12513         var id = Roo.id();
12514         
12515         var cfg = {};
12516         
12517         if(this.inputType != 'hidden'){
12518             cfg.cls = 'form-group' //input-group
12519         }
12520         
12521         var input =  {
12522             tag: 'input',
12523             id : id,
12524             type : this.inputType,
12525             value : this.value,
12526             cls : 'form-control',
12527             placeholder : this.placeholder || '',
12528             autocomplete : this.autocomplete || 'new-password'
12529         };
12530         if (this.inputType == 'file') {
12531             input.style = 'overflow:hidden'; // why not in CSS?
12532         }
12533         
12534         if(this.capture.length){
12535             input.capture = this.capture;
12536         }
12537         
12538         if(this.accept.length){
12539             input.accept = this.accept + "/*";
12540         }
12541         
12542         if(this.align){
12543             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12544         }
12545         
12546         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12547             input.maxLength = this.maxLength;
12548         }
12549         
12550         if (this.disabled) {
12551             input.disabled=true;
12552         }
12553         
12554         if (this.readOnly) {
12555             input.readonly=true;
12556         }
12557         
12558         if (this.name) {
12559             input.name = this.name;
12560         }
12561         
12562         if (this.size) {
12563             input.cls += ' input-' + this.size;
12564         }
12565         
12566         var settings=this;
12567         ['xs','sm','md','lg'].map(function(size){
12568             if (settings[size]) {
12569                 cfg.cls += ' col-' + size + '-' + settings[size];
12570             }
12571         });
12572         
12573         var inputblock = input;
12574         
12575         var feedback = {
12576             tag: 'span',
12577             cls: 'glyphicon form-control-feedback'
12578         };
12579             
12580         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12581             
12582             inputblock = {
12583                 cls : 'has-feedback',
12584                 cn :  [
12585                     input,
12586                     feedback
12587                 ] 
12588             };  
12589         }
12590         
12591         if (this.before || this.after) {
12592             
12593             inputblock = {
12594                 cls : 'input-group',
12595                 cn :  [] 
12596             };
12597             
12598             if (this.before && typeof(this.before) == 'string') {
12599                 
12600                 inputblock.cn.push({
12601                     tag :'span',
12602                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12603                     html : this.before
12604                 });
12605             }
12606             if (this.before && typeof(this.before) == 'object') {
12607                 this.before = Roo.factory(this.before);
12608                 
12609                 inputblock.cn.push({
12610                     tag :'span',
12611                     cls : 'roo-input-before input-group-prepend   input-group-' +
12612                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12613                 });
12614             }
12615             
12616             inputblock.cn.push(input);
12617             
12618             if (this.after && typeof(this.after) == 'string') {
12619                 inputblock.cn.push({
12620                     tag :'span',
12621                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12622                     html : this.after
12623                 });
12624             }
12625             if (this.after && typeof(this.after) == 'object') {
12626                 this.after = Roo.factory(this.after);
12627                 
12628                 inputblock.cn.push({
12629                     tag :'span',
12630                     cls : 'roo-input-after input-group-append  input-group-' +
12631                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12632                 });
12633             }
12634             
12635             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12636                 inputblock.cls += ' has-feedback';
12637                 inputblock.cn.push(feedback);
12638             }
12639         };
12640         var indicator = {
12641             tag : 'i',
12642             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12643             tooltip : 'This field is required'
12644         };
12645         if (this.allowBlank ) {
12646             indicator.style = this.allowBlank ? ' display:none' : '';
12647         }
12648         if (align ==='left' && this.fieldLabel.length) {
12649             
12650             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12651             
12652             cfg.cn = [
12653                 indicator,
12654                 {
12655                     tag: 'label',
12656                     'for' :  id,
12657                     cls : 'control-label col-form-label',
12658                     html : this.fieldLabel
12659
12660                 },
12661                 {
12662                     cls : "", 
12663                     cn: [
12664                         inputblock
12665                     ]
12666                 }
12667             ];
12668             
12669             var labelCfg = cfg.cn[1];
12670             var contentCfg = cfg.cn[2];
12671             
12672             if(this.indicatorpos == 'right'){
12673                 cfg.cn = [
12674                     {
12675                         tag: 'label',
12676                         'for' :  id,
12677                         cls : 'control-label col-form-label',
12678                         cn : [
12679                             {
12680                                 tag : 'span',
12681                                 html : this.fieldLabel
12682                             },
12683                             indicator
12684                         ]
12685                     },
12686                     {
12687                         cls : "",
12688                         cn: [
12689                             inputblock
12690                         ]
12691                     }
12692
12693                 ];
12694                 
12695                 labelCfg = cfg.cn[0];
12696                 contentCfg = cfg.cn[1];
12697             
12698             }
12699             
12700             if(this.labelWidth > 12){
12701                 labelCfg.style = "width: " + this.labelWidth + 'px';
12702             }
12703             
12704             if(this.labelWidth < 13 && this.labelmd == 0){
12705                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12706             }
12707             
12708             if(this.labellg > 0){
12709                 labelCfg.cls += ' col-lg-' + this.labellg;
12710                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12711             }
12712             
12713             if(this.labelmd > 0){
12714                 labelCfg.cls += ' col-md-' + this.labelmd;
12715                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12716             }
12717             
12718             if(this.labelsm > 0){
12719                 labelCfg.cls += ' col-sm-' + this.labelsm;
12720                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12721             }
12722             
12723             if(this.labelxs > 0){
12724                 labelCfg.cls += ' col-xs-' + this.labelxs;
12725                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12726             }
12727             
12728             
12729         } else if ( this.fieldLabel.length) {
12730                 
12731             
12732             
12733             cfg.cn = [
12734                 {
12735                     tag : 'i',
12736                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12737                     tooltip : 'This field is required',
12738                     style : this.allowBlank ? ' display:none' : '' 
12739                 },
12740                 {
12741                     tag: 'label',
12742                    //cls : 'input-group-addon',
12743                     html : this.fieldLabel
12744
12745                 },
12746
12747                inputblock
12748
12749            ];
12750            
12751            if(this.indicatorpos == 'right'){
12752        
12753                 cfg.cn = [
12754                     {
12755                         tag: 'label',
12756                        //cls : 'input-group-addon',
12757                         html : this.fieldLabel
12758
12759                     },
12760                     {
12761                         tag : 'i',
12762                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12763                         tooltip : 'This field is required',
12764                         style : this.allowBlank ? ' display:none' : '' 
12765                     },
12766
12767                    inputblock
12768
12769                ];
12770
12771             }
12772
12773         } else {
12774             
12775             cfg.cn = [
12776
12777                     inputblock
12778
12779             ];
12780                 
12781                 
12782         };
12783         
12784         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12785            cfg.cls += ' navbar-form';
12786         }
12787         
12788         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12789             // on BS4 we do this only if not form 
12790             cfg.cls += ' navbar-form';
12791             cfg.tag = 'li';
12792         }
12793         
12794         return cfg;
12795         
12796     },
12797     /**
12798      * return the real input element.
12799      */
12800     inputEl: function ()
12801     {
12802         return this.el.select('input.form-control',true).first();
12803     },
12804     
12805     tooltipEl : function()
12806     {
12807         return this.inputEl();
12808     },
12809     
12810     indicatorEl : function()
12811     {
12812         if (Roo.bootstrap.version == 4) {
12813             return false; // not enabled in v4 yet.
12814         }
12815         
12816         var indicator = this.el.select('i.roo-required-indicator',true).first();
12817         
12818         if(!indicator){
12819             return false;
12820         }
12821         
12822         return indicator;
12823         
12824     },
12825     
12826     setDisabled : function(v)
12827     {
12828         var i  = this.inputEl().dom;
12829         if (!v) {
12830             i.removeAttribute('disabled');
12831             return;
12832             
12833         }
12834         i.setAttribute('disabled','true');
12835     },
12836     initEvents : function()
12837     {
12838           
12839         this.inputEl().on("keydown" , this.fireKey,  this);
12840         this.inputEl().on("focus", this.onFocus,  this);
12841         this.inputEl().on("blur", this.onBlur,  this);
12842         
12843         this.inputEl().relayEvent('keyup', this);
12844         this.inputEl().relayEvent('paste', this);
12845         
12846         this.indicator = this.indicatorEl();
12847         
12848         if(this.indicator){
12849             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12850         }
12851  
12852         // reference to original value for reset
12853         this.originalValue = this.getValue();
12854         //Roo.form.TextField.superclass.initEvents.call(this);
12855         if(this.validationEvent == 'keyup'){
12856             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12857             this.inputEl().on('keyup', this.filterValidation, this);
12858         }
12859         else if(this.validationEvent !== false){
12860             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12861         }
12862         
12863         if(this.selectOnFocus){
12864             this.on("focus", this.preFocus, this);
12865             
12866         }
12867         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12868             this.inputEl().on("keypress", this.filterKeys, this);
12869         } else {
12870             this.inputEl().relayEvent('keypress', this);
12871         }
12872        /* if(this.grow){
12873             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12874             this.el.on("click", this.autoSize,  this);
12875         }
12876         */
12877         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12878             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12879         }
12880         
12881         if (typeof(this.before) == 'object') {
12882             this.before.render(this.el.select('.roo-input-before',true).first());
12883         }
12884         if (typeof(this.after) == 'object') {
12885             this.after.render(this.el.select('.roo-input-after',true).first());
12886         }
12887         
12888         this.inputEl().on('change', this.onChange, this);
12889         
12890     },
12891     filterValidation : function(e){
12892         if(!e.isNavKeyPress()){
12893             this.validationTask.delay(this.validationDelay);
12894         }
12895     },
12896      /**
12897      * Validates the field value
12898      * @return {Boolean} True if the value is valid, else false
12899      */
12900     validate : function(){
12901         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12902         if(this.disabled || this.validateValue(this.getRawValue())){
12903             this.markValid();
12904             return true;
12905         }
12906         
12907         this.markInvalid();
12908         return false;
12909     },
12910     
12911     
12912     /**
12913      * Validates a value according to the field's validation rules and marks the field as invalid
12914      * if the validation fails
12915      * @param {Mixed} value The value to validate
12916      * @return {Boolean} True if the value is valid, else false
12917      */
12918     validateValue : function(value)
12919     {
12920         if(this.getVisibilityEl().hasClass('hidden')){
12921             return true;
12922         }
12923         
12924         if(value.length < 1)  { // if it's blank
12925             if(this.allowBlank){
12926                 return true;
12927             }
12928             return false;
12929         }
12930         
12931         if(value.length < this.minLength){
12932             return false;
12933         }
12934         if(value.length > this.maxLength){
12935             return false;
12936         }
12937         if(this.vtype){
12938             var vt = Roo.form.VTypes;
12939             if(!vt[this.vtype](value, this)){
12940                 return false;
12941             }
12942         }
12943         if(typeof this.validator == "function"){
12944             var msg = this.validator(value);
12945             if(msg !== true){
12946                 return false;
12947             }
12948             if (typeof(msg) == 'string') {
12949                 this.invalidText = msg;
12950             }
12951         }
12952         
12953         if(this.regex && !this.regex.test(value)){
12954             return false;
12955         }
12956         
12957         return true;
12958     },
12959     
12960      // private
12961     fireKey : function(e){
12962         //Roo.log('field ' + e.getKey());
12963         if(e.isNavKeyPress()){
12964             this.fireEvent("specialkey", this, e);
12965         }
12966     },
12967     focus : function (selectText){
12968         if(this.rendered){
12969             this.inputEl().focus();
12970             if(selectText === true){
12971                 this.inputEl().dom.select();
12972             }
12973         }
12974         return this;
12975     } ,
12976     
12977     onFocus : function(){
12978         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12979            // this.el.addClass(this.focusClass);
12980         }
12981         if(!this.hasFocus){
12982             this.hasFocus = true;
12983             this.startValue = this.getValue();
12984             this.fireEvent("focus", this);
12985         }
12986     },
12987     
12988     beforeBlur : Roo.emptyFn,
12989
12990     
12991     // private
12992     onBlur : function(){
12993         this.beforeBlur();
12994         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12995             //this.el.removeClass(this.focusClass);
12996         }
12997         this.hasFocus = false;
12998         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12999             this.validate();
13000         }
13001         var v = this.getValue();
13002         if(String(v) !== String(this.startValue)){
13003             this.fireEvent('change', this, v, this.startValue);
13004         }
13005         this.fireEvent("blur", this);
13006     },
13007     
13008     onChange : function(e)
13009     {
13010         var v = this.getValue();
13011         if(String(v) !== String(this.startValue)){
13012             this.fireEvent('change', this, v, this.startValue);
13013         }
13014         
13015     },
13016     
13017     /**
13018      * Resets the current field value to the originally loaded value and clears any validation messages
13019      */
13020     reset : function(){
13021         this.setValue(this.originalValue);
13022         this.validate();
13023     },
13024      /**
13025      * Returns the name of the field
13026      * @return {Mixed} name The name field
13027      */
13028     getName: function(){
13029         return this.name;
13030     },
13031      /**
13032      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13033      * @return {Mixed} value The field value
13034      */
13035     getValue : function(){
13036         
13037         var v = this.inputEl().getValue();
13038         
13039         return v;
13040     },
13041     /**
13042      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13043      * @return {Mixed} value The field value
13044      */
13045     getRawValue : function(){
13046         var v = this.inputEl().getValue();
13047         
13048         return v;
13049     },
13050     
13051     /**
13052      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13053      * @param {Mixed} value The value to set
13054      */
13055     setRawValue : function(v){
13056         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13057     },
13058     
13059     selectText : function(start, end){
13060         var v = this.getRawValue();
13061         if(v.length > 0){
13062             start = start === undefined ? 0 : start;
13063             end = end === undefined ? v.length : end;
13064             var d = this.inputEl().dom;
13065             if(d.setSelectionRange){
13066                 d.setSelectionRange(start, end);
13067             }else if(d.createTextRange){
13068                 var range = d.createTextRange();
13069                 range.moveStart("character", start);
13070                 range.moveEnd("character", v.length-end);
13071                 range.select();
13072             }
13073         }
13074     },
13075     
13076     /**
13077      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13078      * @param {Mixed} value The value to set
13079      */
13080     setValue : function(v){
13081         this.value = v;
13082         if(this.rendered){
13083             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13084             this.validate();
13085         }
13086     },
13087     
13088     /*
13089     processValue : function(value){
13090         if(this.stripCharsRe){
13091             var newValue = value.replace(this.stripCharsRe, '');
13092             if(newValue !== value){
13093                 this.setRawValue(newValue);
13094                 return newValue;
13095             }
13096         }
13097         return value;
13098     },
13099   */
13100     preFocus : function(){
13101         
13102         if(this.selectOnFocus){
13103             this.inputEl().dom.select();
13104         }
13105     },
13106     filterKeys : function(e){
13107         var k = e.getKey();
13108         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13109             return;
13110         }
13111         var c = e.getCharCode(), cc = String.fromCharCode(c);
13112         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13113             return;
13114         }
13115         if(!this.maskRe.test(cc)){
13116             e.stopEvent();
13117         }
13118     },
13119      /**
13120      * Clear any invalid styles/messages for this field
13121      */
13122     clearInvalid : function(){
13123         
13124         if(!this.el || this.preventMark){ // not rendered
13125             return;
13126         }
13127         
13128         
13129         this.el.removeClass([this.invalidClass, 'is-invalid']);
13130         
13131         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13132             
13133             var feedback = this.el.select('.form-control-feedback', true).first();
13134             
13135             if(feedback){
13136                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13137             }
13138             
13139         }
13140         
13141         if(this.indicator){
13142             this.indicator.removeClass('visible');
13143             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13144         }
13145         
13146         this.fireEvent('valid', this);
13147     },
13148     
13149      /**
13150      * Mark this field as valid
13151      */
13152     markValid : function()
13153     {
13154         if(!this.el  || this.preventMark){ // not rendered...
13155             return;
13156         }
13157         
13158         this.el.removeClass([this.invalidClass, this.validClass]);
13159         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13160
13161         var feedback = this.el.select('.form-control-feedback', true).first();
13162             
13163         if(feedback){
13164             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13165         }
13166         
13167         if(this.indicator){
13168             this.indicator.removeClass('visible');
13169             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13170         }
13171         
13172         if(this.disabled){
13173             return;
13174         }
13175         
13176            
13177         if(this.allowBlank && !this.getRawValue().length){
13178             return;
13179         }
13180         if (Roo.bootstrap.version == 3) {
13181             this.el.addClass(this.validClass);
13182         } else {
13183             this.inputEl().addClass('is-valid');
13184         }
13185
13186         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13187             
13188             var feedback = this.el.select('.form-control-feedback', true).first();
13189             
13190             if(feedback){
13191                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13192                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13193             }
13194             
13195         }
13196         
13197         this.fireEvent('valid', this);
13198     },
13199     
13200      /**
13201      * Mark this field as invalid
13202      * @param {String} msg The validation message
13203      */
13204     markInvalid : function(msg)
13205     {
13206         if(!this.el  || this.preventMark){ // not rendered
13207             return;
13208         }
13209         
13210         this.el.removeClass([this.invalidClass, this.validClass]);
13211         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13212         
13213         var feedback = this.el.select('.form-control-feedback', true).first();
13214             
13215         if(feedback){
13216             this.el.select('.form-control-feedback', true).first().removeClass(
13217                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13218         }
13219
13220         if(this.disabled){
13221             return;
13222         }
13223         
13224         if(this.allowBlank && !this.getRawValue().length){
13225             return;
13226         }
13227         
13228         if(this.indicator){
13229             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13230             this.indicator.addClass('visible');
13231         }
13232         if (Roo.bootstrap.version == 3) {
13233             this.el.addClass(this.invalidClass);
13234         } else {
13235             this.inputEl().addClass('is-invalid');
13236         }
13237         
13238         
13239         
13240         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13241             
13242             var feedback = this.el.select('.form-control-feedback', true).first();
13243             
13244             if(feedback){
13245                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13246                 
13247                 if(this.getValue().length || this.forceFeedback){
13248                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13249                 }
13250                 
13251             }
13252             
13253         }
13254         
13255         this.fireEvent('invalid', this, msg);
13256     },
13257     // private
13258     SafariOnKeyDown : function(event)
13259     {
13260         // this is a workaround for a password hang bug on chrome/ webkit.
13261         if (this.inputEl().dom.type != 'password') {
13262             return;
13263         }
13264         
13265         var isSelectAll = false;
13266         
13267         if(this.inputEl().dom.selectionEnd > 0){
13268             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13269         }
13270         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13271             event.preventDefault();
13272             this.setValue('');
13273             return;
13274         }
13275         
13276         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13277             
13278             event.preventDefault();
13279             // this is very hacky as keydown always get's upper case.
13280             //
13281             var cc = String.fromCharCode(event.getCharCode());
13282             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13283             
13284         }
13285     },
13286     adjustWidth : function(tag, w){
13287         tag = tag.toLowerCase();
13288         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13289             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13290                 if(tag == 'input'){
13291                     return w + 2;
13292                 }
13293                 if(tag == 'textarea'){
13294                     return w-2;
13295                 }
13296             }else if(Roo.isOpera){
13297                 if(tag == 'input'){
13298                     return w + 2;
13299                 }
13300                 if(tag == 'textarea'){
13301                     return w-2;
13302                 }
13303             }
13304         }
13305         return w;
13306     },
13307     
13308     setFieldLabel : function(v)
13309     {
13310         if(!this.rendered){
13311             return;
13312         }
13313         
13314         if(this.indicatorEl()){
13315             var ar = this.el.select('label > span',true);
13316             
13317             if (ar.elements.length) {
13318                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13319                 this.fieldLabel = v;
13320                 return;
13321             }
13322             
13323             var br = this.el.select('label',true);
13324             
13325             if(br.elements.length) {
13326                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13327                 this.fieldLabel = v;
13328                 return;
13329             }
13330             
13331             Roo.log('Cannot Found any of label > span || label in input');
13332             return;
13333         }
13334         
13335         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13336         this.fieldLabel = v;
13337         
13338         
13339     }
13340 });
13341
13342  
13343 /*
13344  * - LGPL
13345  *
13346  * Input
13347  * 
13348  */
13349
13350 /**
13351  * @class Roo.bootstrap.form.TextArea
13352  * @extends Roo.bootstrap.form.Input
13353  * Bootstrap TextArea class
13354  * @cfg {Number} cols Specifies the visible width of a text area
13355  * @cfg {Number} rows Specifies the visible number of lines in a text area
13356  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13357  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13358  * @cfg {string} html text
13359  * 
13360  * @constructor
13361  * Create a new TextArea
13362  * @param {Object} config The config object
13363  */
13364
13365 Roo.bootstrap.form.TextArea = function(config){
13366     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13367    
13368 };
13369
13370 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13371      
13372     cols : false,
13373     rows : 5,
13374     readOnly : false,
13375     warp : 'soft',
13376     resize : false,
13377     value: false,
13378     html: false,
13379     
13380     getAutoCreate : function(){
13381         
13382         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13383         
13384         var id = Roo.id();
13385         
13386         var cfg = {};
13387         
13388         if(this.inputType != 'hidden'){
13389             cfg.cls = 'form-group' //input-group
13390         }
13391         
13392         var input =  {
13393             tag: 'textarea',
13394             id : id,
13395             warp : this.warp,
13396             rows : this.rows,
13397             value : this.value || '',
13398             html: this.html || '',
13399             cls : 'form-control',
13400             placeholder : this.placeholder || '' 
13401             
13402         };
13403         
13404         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13405             input.maxLength = this.maxLength;
13406         }
13407         
13408         if(this.resize){
13409             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13410         }
13411         
13412         if(this.cols){
13413             input.cols = this.cols;
13414         }
13415         
13416         if (this.readOnly) {
13417             input.readonly = true;
13418         }
13419         
13420         if (this.name) {
13421             input.name = this.name;
13422         }
13423         
13424         if (this.size) {
13425             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13426         }
13427         
13428         var settings=this;
13429         ['xs','sm','md','lg'].map(function(size){
13430             if (settings[size]) {
13431                 cfg.cls += ' col-' + size + '-' + settings[size];
13432             }
13433         });
13434         
13435         var inputblock = input;
13436         
13437         if(this.hasFeedback && !this.allowBlank){
13438             
13439             var feedback = {
13440                 tag: 'span',
13441                 cls: 'glyphicon form-control-feedback'
13442             };
13443
13444             inputblock = {
13445                 cls : 'has-feedback',
13446                 cn :  [
13447                     input,
13448                     feedback
13449                 ] 
13450             };  
13451         }
13452         
13453         
13454         if (this.before || this.after) {
13455             
13456             inputblock = {
13457                 cls : 'input-group',
13458                 cn :  [] 
13459             };
13460             if (this.before) {
13461                 inputblock.cn.push({
13462                     tag :'span',
13463                     cls : 'input-group-addon',
13464                     html : this.before
13465                 });
13466             }
13467             
13468             inputblock.cn.push(input);
13469             
13470             if(this.hasFeedback && !this.allowBlank){
13471                 inputblock.cls += ' has-feedback';
13472                 inputblock.cn.push(feedback);
13473             }
13474             
13475             if (this.after) {
13476                 inputblock.cn.push({
13477                     tag :'span',
13478                     cls : 'input-group-addon',
13479                     html : this.after
13480                 });
13481             }
13482             
13483         }
13484         
13485         if (align ==='left' && this.fieldLabel.length) {
13486             cfg.cn = [
13487                 {
13488                     tag: 'label',
13489                     'for' :  id,
13490                     cls : 'control-label',
13491                     html : this.fieldLabel
13492                 },
13493                 {
13494                     cls : "",
13495                     cn: [
13496                         inputblock
13497                     ]
13498                 }
13499
13500             ];
13501             
13502             if(this.labelWidth > 12){
13503                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13504             }
13505
13506             if(this.labelWidth < 13 && this.labelmd == 0){
13507                 this.labelmd = this.labelWidth;
13508             }
13509
13510             if(this.labellg > 0){
13511                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13512                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13513             }
13514
13515             if(this.labelmd > 0){
13516                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13517                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13518             }
13519
13520             if(this.labelsm > 0){
13521                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13522                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13523             }
13524
13525             if(this.labelxs > 0){
13526                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13527                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13528             }
13529             
13530         } else if ( this.fieldLabel.length) {
13531             cfg.cn = [
13532
13533                {
13534                    tag: 'label',
13535                    //cls : 'input-group-addon',
13536                    html : this.fieldLabel
13537
13538                },
13539
13540                inputblock
13541
13542            ];
13543
13544         } else {
13545
13546             cfg.cn = [
13547
13548                 inputblock
13549
13550             ];
13551                 
13552         }
13553         
13554         if (this.disabled) {
13555             input.disabled=true;
13556         }
13557         
13558         return cfg;
13559         
13560     },
13561     /**
13562      * return the real textarea element.
13563      */
13564     inputEl: function ()
13565     {
13566         return this.el.select('textarea.form-control',true).first();
13567     },
13568     
13569     /**
13570      * Clear any invalid styles/messages for this field
13571      */
13572     clearInvalid : function()
13573     {
13574         
13575         if(!this.el || this.preventMark){ // not rendered
13576             return;
13577         }
13578         
13579         var label = this.el.select('label', true).first();
13580         var icon = this.el.select('i.fa-star', true).first();
13581         
13582         if(label && icon){
13583             icon.remove();
13584         }
13585         this.el.removeClass( this.validClass);
13586         this.inputEl().removeClass('is-invalid');
13587          
13588         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13589             
13590             var feedback = this.el.select('.form-control-feedback', true).first();
13591             
13592             if(feedback){
13593                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13594             }
13595             
13596         }
13597         
13598         this.fireEvent('valid', this);
13599     },
13600     
13601      /**
13602      * Mark this field as valid
13603      */
13604     markValid : function()
13605     {
13606         if(!this.el  || this.preventMark){ // not rendered
13607             return;
13608         }
13609         
13610         this.el.removeClass([this.invalidClass, this.validClass]);
13611         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13612         
13613         var feedback = this.el.select('.form-control-feedback', true).first();
13614             
13615         if(feedback){
13616             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13617         }
13618
13619         if(this.disabled || this.allowBlank){
13620             return;
13621         }
13622         
13623         var label = this.el.select('label', true).first();
13624         var icon = this.el.select('i.fa-star', true).first();
13625         
13626         if(label && icon){
13627             icon.remove();
13628         }
13629         if (Roo.bootstrap.version == 3) {
13630             this.el.addClass(this.validClass);
13631         } else {
13632             this.inputEl().addClass('is-valid');
13633         }
13634         
13635         
13636         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13637             
13638             var feedback = this.el.select('.form-control-feedback', true).first();
13639             
13640             if(feedback){
13641                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13642                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13643             }
13644             
13645         }
13646         
13647         this.fireEvent('valid', this);
13648     },
13649     
13650      /**
13651      * Mark this field as invalid
13652      * @param {String} msg The validation message
13653      */
13654     markInvalid : function(msg)
13655     {
13656         if(!this.el  || this.preventMark){ // not rendered
13657             return;
13658         }
13659         
13660         this.el.removeClass([this.invalidClass, this.validClass]);
13661         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13662         
13663         var feedback = this.el.select('.form-control-feedback', true).first();
13664             
13665         if(feedback){
13666             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13667         }
13668
13669         if(this.disabled || this.allowBlank){
13670             return;
13671         }
13672         
13673         var label = this.el.select('label', true).first();
13674         var icon = this.el.select('i.fa-star', true).first();
13675         
13676         if(!this.getValue().length && label && !icon){
13677             this.el.createChild({
13678                 tag : 'i',
13679                 cls : 'text-danger fa fa-lg fa-star',
13680                 tooltip : 'This field is required',
13681                 style : 'margin-right:5px;'
13682             }, label, true);
13683         }
13684         
13685         if (Roo.bootstrap.version == 3) {
13686             this.el.addClass(this.invalidClass);
13687         } else {
13688             this.inputEl().addClass('is-invalid');
13689         }
13690         
13691         // fixme ... this may be depricated need to test..
13692         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13693             
13694             var feedback = this.el.select('.form-control-feedback', true).first();
13695             
13696             if(feedback){
13697                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13698                 
13699                 if(this.getValue().length || this.forceFeedback){
13700                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13701                 }
13702                 
13703             }
13704             
13705         }
13706         
13707         this.fireEvent('invalid', this, msg);
13708     }
13709 });
13710
13711  
13712 /*
13713  * - LGPL
13714  *
13715  * trigger field - base class for combo..
13716  * 
13717  */
13718  
13719 /**
13720  * @class Roo.bootstrap.form.TriggerField
13721  * @extends Roo.bootstrap.form.Input
13722  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13723  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13724  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13725  * for which you can provide a custom implementation.  For example:
13726  * <pre><code>
13727 var trigger = new Roo.bootstrap.form.TriggerField();
13728 trigger.onTriggerClick = myTriggerFn;
13729 trigger.applyTo('my-field');
13730 </code></pre>
13731  *
13732  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13733  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13734  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13735  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13736  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13737
13738  * @constructor
13739  * Create a new TriggerField.
13740  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13741  * to the base TextField)
13742  */
13743 Roo.bootstrap.form.TriggerField = function(config){
13744     this.mimicing = false;
13745     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13746 };
13747
13748 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13749     /**
13750      * @cfg {String} triggerClass A CSS class to apply to the trigger
13751      */
13752      /**
13753      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13754      */
13755     hideTrigger:false,
13756
13757     /**
13758      * @cfg {Boolean} removable (true|false) special filter default false
13759      */
13760     removable : false,
13761     
13762     /** @cfg {Boolean} grow @hide */
13763     /** @cfg {Number} growMin @hide */
13764     /** @cfg {Number} growMax @hide */
13765
13766     /**
13767      * @hide 
13768      * @method
13769      */
13770     autoSize: Roo.emptyFn,
13771     // private
13772     monitorTab : true,
13773     // private
13774     deferHeight : true,
13775
13776     
13777     actionMode : 'wrap',
13778     
13779     caret : false,
13780     
13781     
13782     getAutoCreate : function(){
13783        
13784         var align = this.labelAlign || this.parentLabelAlign();
13785         
13786         var id = Roo.id();
13787         
13788         var cfg = {
13789             cls: 'form-group' //input-group
13790         };
13791         
13792         
13793         var input =  {
13794             tag: 'input',
13795             id : id,
13796             type : this.inputType,
13797             cls : 'form-control',
13798             autocomplete: 'new-password',
13799             placeholder : this.placeholder || '' 
13800             
13801         };
13802         if (this.name) {
13803             input.name = this.name;
13804         }
13805         if (this.size) {
13806             input.cls += ' input-' + this.size;
13807         }
13808         
13809         if (this.disabled) {
13810             input.disabled=true;
13811         }
13812         
13813         var inputblock = input;
13814         
13815         if(this.hasFeedback && !this.allowBlank){
13816             
13817             var feedback = {
13818                 tag: 'span',
13819                 cls: 'glyphicon form-control-feedback'
13820             };
13821             
13822             if(this.removable && !this.editable  ){
13823                 inputblock = {
13824                     cls : 'has-feedback',
13825                     cn :  [
13826                         inputblock,
13827                         {
13828                             tag: 'button',
13829                             html : 'x',
13830                             cls : 'roo-combo-removable-btn close'
13831                         },
13832                         feedback
13833                     ] 
13834                 };
13835             } else {
13836                 inputblock = {
13837                     cls : 'has-feedback',
13838                     cn :  [
13839                         inputblock,
13840                         feedback
13841                     ] 
13842                 };
13843             }
13844
13845         } else {
13846             if(this.removable && !this.editable ){
13847                 inputblock = {
13848                     cls : 'roo-removable',
13849                     cn :  [
13850                         inputblock,
13851                         {
13852                             tag: 'button',
13853                             html : 'x',
13854                             cls : 'roo-combo-removable-btn close'
13855                         }
13856                     ] 
13857                 };
13858             }
13859         }
13860         
13861         if (this.before || this.after) {
13862             
13863             inputblock = {
13864                 cls : 'input-group',
13865                 cn :  [] 
13866             };
13867             if (this.before) {
13868                 inputblock.cn.push({
13869                     tag :'span',
13870                     cls : 'input-group-addon input-group-prepend input-group-text',
13871                     html : this.before
13872                 });
13873             }
13874             
13875             inputblock.cn.push(input);
13876             
13877             if(this.hasFeedback && !this.allowBlank){
13878                 inputblock.cls += ' has-feedback';
13879                 inputblock.cn.push(feedback);
13880             }
13881             
13882             if (this.after) {
13883                 inputblock.cn.push({
13884                     tag :'span',
13885                     cls : 'input-group-addon input-group-append input-group-text',
13886                     html : this.after
13887                 });
13888             }
13889             
13890         };
13891         
13892       
13893         
13894         var ibwrap = inputblock;
13895         
13896         if(this.multiple){
13897             ibwrap = {
13898                 tag: 'ul',
13899                 cls: 'roo-select2-choices',
13900                 cn:[
13901                     {
13902                         tag: 'li',
13903                         cls: 'roo-select2-search-field',
13904                         cn: [
13905
13906                             inputblock
13907                         ]
13908                     }
13909                 ]
13910             };
13911                 
13912         }
13913         
13914         var combobox = {
13915             cls: 'roo-select2-container input-group',
13916             cn: [
13917                  {
13918                     tag: 'input',
13919                     type : 'hidden',
13920                     cls: 'form-hidden-field'
13921                 },
13922                 ibwrap
13923             ]
13924         };
13925         
13926         if(!this.multiple && this.showToggleBtn){
13927             
13928             var caret = {
13929                         tag: 'span',
13930                         cls: 'caret'
13931              };
13932             if (this.caret != false) {
13933                 caret = {
13934                      tag: 'i',
13935                      cls: 'fa fa-' + this.caret
13936                 };
13937                 
13938             }
13939             
13940             combobox.cn.push({
13941                 tag :'span',
13942                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13943                 cn : [
13944                     Roo.bootstrap.version == 3 ? caret : '',
13945                     {
13946                         tag: 'span',
13947                         cls: 'combobox-clear',
13948                         cn  : [
13949                             {
13950                                 tag : 'i',
13951                                 cls: 'icon-remove'
13952                             }
13953                         ]
13954                     }
13955                 ]
13956
13957             })
13958         }
13959         
13960         if(this.multiple){
13961             combobox.cls += ' roo-select2-container-multi';
13962         }
13963          var indicator = {
13964             tag : 'i',
13965             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13966             tooltip : 'This field is required'
13967         };
13968         if (Roo.bootstrap.version == 4) {
13969             indicator = {
13970                 tag : 'i',
13971                 style : 'display:none'
13972             };
13973         }
13974         
13975         
13976         if (align ==='left' && this.fieldLabel.length) {
13977             
13978             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13979
13980             cfg.cn = [
13981                 indicator,
13982                 {
13983                     tag: 'label',
13984                     'for' :  id,
13985                     cls : 'control-label',
13986                     html : this.fieldLabel
13987
13988                 },
13989                 {
13990                     cls : "", 
13991                     cn: [
13992                         combobox
13993                     ]
13994                 }
13995
13996             ];
13997             
13998             var labelCfg = cfg.cn[1];
13999             var contentCfg = cfg.cn[2];
14000             
14001             if(this.indicatorpos == 'right'){
14002                 cfg.cn = [
14003                     {
14004                         tag: 'label',
14005                         'for' :  id,
14006                         cls : 'control-label',
14007                         cn : [
14008                             {
14009                                 tag : 'span',
14010                                 html : this.fieldLabel
14011                             },
14012                             indicator
14013                         ]
14014                     },
14015                     {
14016                         cls : "", 
14017                         cn: [
14018                             combobox
14019                         ]
14020                     }
14021
14022                 ];
14023                 
14024                 labelCfg = cfg.cn[0];
14025                 contentCfg = cfg.cn[1];
14026             }
14027             
14028             if(this.labelWidth > 12){
14029                 labelCfg.style = "width: " + this.labelWidth + 'px';
14030             }
14031             
14032             if(this.labelWidth < 13 && this.labelmd == 0){
14033                 this.labelmd = this.labelWidth;
14034             }
14035             
14036             if(this.labellg > 0){
14037                 labelCfg.cls += ' col-lg-' + this.labellg;
14038                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14039             }
14040             
14041             if(this.labelmd > 0){
14042                 labelCfg.cls += ' col-md-' + this.labelmd;
14043                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14044             }
14045             
14046             if(this.labelsm > 0){
14047                 labelCfg.cls += ' col-sm-' + this.labelsm;
14048                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14049             }
14050             
14051             if(this.labelxs > 0){
14052                 labelCfg.cls += ' col-xs-' + this.labelxs;
14053                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14054             }
14055             
14056         } else if ( this.fieldLabel.length) {
14057 //                Roo.log(" label");
14058             cfg.cn = [
14059                 indicator,
14060                {
14061                    tag: 'label',
14062                    //cls : 'input-group-addon',
14063                    html : this.fieldLabel
14064
14065                },
14066
14067                combobox
14068
14069             ];
14070             
14071             if(this.indicatorpos == 'right'){
14072                 
14073                 cfg.cn = [
14074                     {
14075                        tag: 'label',
14076                        cn : [
14077                            {
14078                                tag : 'span',
14079                                html : this.fieldLabel
14080                            },
14081                            indicator
14082                        ]
14083
14084                     },
14085                     combobox
14086
14087                 ];
14088
14089             }
14090
14091         } else {
14092             
14093 //                Roo.log(" no label && no align");
14094                 cfg = combobox
14095                      
14096                 
14097         }
14098         
14099         var settings=this;
14100         ['xs','sm','md','lg'].map(function(size){
14101             if (settings[size]) {
14102                 cfg.cls += ' col-' + size + '-' + settings[size];
14103             }
14104         });
14105         
14106         return cfg;
14107         
14108     },
14109     
14110     
14111     
14112     // private
14113     onResize : function(w, h){
14114 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14115 //        if(typeof w == 'number'){
14116 //            var x = w - this.trigger.getWidth();
14117 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14118 //            this.trigger.setStyle('left', x+'px');
14119 //        }
14120     },
14121
14122     // private
14123     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14124
14125     // private
14126     getResizeEl : function(){
14127         return this.inputEl();
14128     },
14129
14130     // private
14131     getPositionEl : function(){
14132         return this.inputEl();
14133     },
14134
14135     // private
14136     alignErrorIcon : function(){
14137         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14138     },
14139
14140     // private
14141     initEvents : function(){
14142         
14143         this.createList();
14144         
14145         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14146         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14147         if(!this.multiple && this.showToggleBtn){
14148             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14149             if(this.hideTrigger){
14150                 this.trigger.setDisplayed(false);
14151             }
14152             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14153         }
14154         
14155         if(this.multiple){
14156             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14157         }
14158         
14159         if(this.removable && !this.editable && !this.tickable){
14160             var close = this.closeTriggerEl();
14161             
14162             if(close){
14163                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14164                 close.on('click', this.removeBtnClick, this, close);
14165             }
14166         }
14167         
14168         //this.trigger.addClassOnOver('x-form-trigger-over');
14169         //this.trigger.addClassOnClick('x-form-trigger-click');
14170         
14171         //if(!this.width){
14172         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14173         //}
14174     },
14175     
14176     closeTriggerEl : function()
14177     {
14178         var close = this.el.select('.roo-combo-removable-btn', true).first();
14179         return close ? close : false;
14180     },
14181     
14182     removeBtnClick : function(e, h, el)
14183     {
14184         e.preventDefault();
14185         
14186         if(this.fireEvent("remove", this) !== false){
14187             this.reset();
14188             this.fireEvent("afterremove", this)
14189         }
14190     },
14191     
14192     createList : function()
14193     {
14194         this.list = Roo.get(document.body).createChild({
14195             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14196             cls: 'typeahead typeahead-long dropdown-menu shadow',
14197             style: 'display:none'
14198         });
14199         
14200         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14201         
14202     },
14203
14204     // private
14205     initTrigger : function(){
14206        
14207     },
14208
14209     // private
14210     onDestroy : function(){
14211         if(this.trigger){
14212             this.trigger.removeAllListeners();
14213           //  this.trigger.remove();
14214         }
14215         //if(this.wrap){
14216         //    this.wrap.remove();
14217         //}
14218         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14219     },
14220
14221     // private
14222     onFocus : function(){
14223         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14224         /*
14225         if(!this.mimicing){
14226             this.wrap.addClass('x-trigger-wrap-focus');
14227             this.mimicing = true;
14228             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14229             if(this.monitorTab){
14230                 this.el.on("keydown", this.checkTab, this);
14231             }
14232         }
14233         */
14234     },
14235
14236     // private
14237     checkTab : function(e){
14238         if(e.getKey() == e.TAB){
14239             this.triggerBlur();
14240         }
14241     },
14242
14243     // private
14244     onBlur : function(){
14245         // do nothing
14246     },
14247
14248     // private
14249     mimicBlur : function(e, t){
14250         /*
14251         if(!this.wrap.contains(t) && this.validateBlur()){
14252             this.triggerBlur();
14253         }
14254         */
14255     },
14256
14257     // private
14258     triggerBlur : function(){
14259         this.mimicing = false;
14260         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14261         if(this.monitorTab){
14262             this.el.un("keydown", this.checkTab, this);
14263         }
14264         //this.wrap.removeClass('x-trigger-wrap-focus');
14265         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14266     },
14267
14268     // private
14269     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14270     validateBlur : function(e, t){
14271         return true;
14272     },
14273
14274     // private
14275     onDisable : function(){
14276         this.inputEl().dom.disabled = true;
14277         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14278         //if(this.wrap){
14279         //    this.wrap.addClass('x-item-disabled');
14280         //}
14281     },
14282
14283     // private
14284     onEnable : function(){
14285         this.inputEl().dom.disabled = false;
14286         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14287         //if(this.wrap){
14288         //    this.el.removeClass('x-item-disabled');
14289         //}
14290     },
14291
14292     // private
14293     onShow : function(){
14294         var ae = this.getActionEl();
14295         
14296         if(ae){
14297             ae.dom.style.display = '';
14298             ae.dom.style.visibility = 'visible';
14299         }
14300     },
14301
14302     // private
14303     
14304     onHide : function(){
14305         var ae = this.getActionEl();
14306         ae.dom.style.display = 'none';
14307     },
14308
14309     /**
14310      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14311      * by an implementing function.
14312      * @method
14313      * @param {EventObject} e
14314      */
14315     onTriggerClick : Roo.emptyFn
14316 });
14317  
14318 /*
14319 * Licence: LGPL
14320 */
14321
14322 /**
14323  * @class Roo.bootstrap.form.CardUploader
14324  * @extends Roo.bootstrap.Button
14325  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14326  * @cfg {Number} errorTimeout default 3000
14327  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14328  * @cfg {Array}  html The button text.
14329
14330  *
14331  * @constructor
14332  * Create a new CardUploader
14333  * @param {Object} config The config object
14334  */
14335
14336 Roo.bootstrap.form.CardUploader = function(config){
14337     
14338  
14339     
14340     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14341     
14342     
14343     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14344         return r.data.id
14345      });
14346     
14347      this.addEvents({
14348          // raw events
14349         /**
14350          * @event preview
14351          * When a image is clicked on - and needs to display a slideshow or similar..
14352          * @param {Roo.bootstrap.Card} this
14353          * @param {Object} The image information data 
14354          *
14355          */
14356         'preview' : true,
14357          /**
14358          * @event download
14359          * When a the download link is clicked
14360          * @param {Roo.bootstrap.Card} this
14361          * @param {Object} The image information data  contains 
14362          */
14363         'download' : true
14364         
14365     });
14366 };
14367  
14368 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14369     
14370      
14371     errorTimeout : 3000,
14372      
14373     images : false,
14374    
14375     fileCollection : false,
14376     allowBlank : true,
14377     
14378     getAutoCreate : function()
14379     {
14380         
14381         var cfg =  {
14382             cls :'form-group' ,
14383             cn : [
14384                
14385                 {
14386                     tag: 'label',
14387                    //cls : 'input-group-addon',
14388                     html : this.fieldLabel
14389
14390                 },
14391
14392                 {
14393                     tag: 'input',
14394                     type : 'hidden',
14395                     name : this.name,
14396                     value : this.value,
14397                     cls : 'd-none  form-control'
14398                 },
14399                 
14400                 {
14401                     tag: 'input',
14402                     multiple : 'multiple',
14403                     type : 'file',
14404                     cls : 'd-none  roo-card-upload-selector'
14405                 },
14406                 
14407                 {
14408                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14409                 },
14410                 {
14411                     cls : 'card-columns roo-card-uploader-container'
14412                 }
14413
14414             ]
14415         };
14416            
14417          
14418         return cfg;
14419     },
14420     
14421     getChildContainer : function() /// what children are added to.
14422     {
14423         return this.containerEl;
14424     },
14425    
14426     getButtonContainer : function() /// what children are added to.
14427     {
14428         return this.el.select(".roo-card-uploader-button-container").first();
14429     },
14430    
14431     initEvents : function()
14432     {
14433         
14434         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14435         
14436         var t = this;
14437         this.addxtype({
14438             xns: Roo.bootstrap,
14439
14440             xtype : 'Button',
14441             container_method : 'getButtonContainer' ,            
14442             html :  this.html, // fix changable?
14443             cls : 'w-100 ',
14444             listeners : {
14445                 'click' : function(btn, e) {
14446                     t.onClick(e);
14447                 }
14448             }
14449         });
14450         
14451         
14452         
14453         
14454         this.urlAPI = (window.createObjectURL && window) || 
14455                                 (window.URL && URL.revokeObjectURL && URL) || 
14456                                 (window.webkitURL && webkitURL);
14457                         
14458          
14459          
14460          
14461         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14462         
14463         this.selectorEl.on('change', this.onFileSelected, this);
14464         if (this.images) {
14465             var t = this;
14466             this.images.forEach(function(img) {
14467                 t.addCard(img)
14468             });
14469             this.images = false;
14470         }
14471         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14472          
14473        
14474     },
14475     
14476    
14477     onClick : function(e)
14478     {
14479         e.preventDefault();
14480          
14481         this.selectorEl.dom.click();
14482          
14483     },
14484     
14485     onFileSelected : function(e)
14486     {
14487         e.preventDefault();
14488         
14489         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14490             return;
14491         }
14492         
14493         Roo.each(this.selectorEl.dom.files, function(file){    
14494             this.addFile(file);
14495         }, this);
14496          
14497     },
14498     
14499       
14500     
14501       
14502     
14503     addFile : function(file)
14504     {
14505            
14506         if(typeof(file) === 'string'){
14507             throw "Add file by name?"; // should not happen
14508             return;
14509         }
14510         
14511         if(!file || !this.urlAPI){
14512             return;
14513         }
14514         
14515         // file;
14516         // file.type;
14517         
14518         var _this = this;
14519         
14520         
14521         var url = _this.urlAPI.createObjectURL( file);
14522            
14523         this.addCard({
14524             id : Roo.bootstrap.form.CardUploader.ID--,
14525             is_uploaded : false,
14526             src : url,
14527             srcfile : file,
14528             title : file.name,
14529             mimetype : file.type,
14530             preview : false,
14531             is_deleted : 0
14532         });
14533         
14534     },
14535     
14536     /**
14537      * addCard - add an Attachment to the uploader
14538      * @param data - the data about the image to upload
14539      *
14540      * {
14541           id : 123
14542           title : "Title of file",
14543           is_uploaded : false,
14544           src : "http://.....",
14545           srcfile : { the File upload object },
14546           mimetype : file.type,
14547           preview : false,
14548           is_deleted : 0
14549           .. any other data...
14550         }
14551      *
14552      * 
14553     */
14554     
14555     addCard : function (data)
14556     {
14557         // hidden input element?
14558         // if the file is not an image...
14559         //then we need to use something other that and header_image
14560         var t = this;
14561         //   remove.....
14562         var footer = [
14563             {
14564                 xns : Roo.bootstrap,
14565                 xtype : 'CardFooter',
14566                  items: [
14567                     {
14568                         xns : Roo.bootstrap,
14569                         xtype : 'Element',
14570                         cls : 'd-flex',
14571                         items : [
14572                             
14573                             {
14574                                 xns : Roo.bootstrap,
14575                                 xtype : 'Button',
14576                                 html : String.format("<small>{0}</small>", data.title),
14577                                 cls : 'col-10 text-left',
14578                                 size: 'sm',
14579                                 weight: 'link',
14580                                 fa : 'download',
14581                                 listeners : {
14582                                     click : function() {
14583                                      
14584                                         t.fireEvent( "download", t, data );
14585                                     }
14586                                 }
14587                             },
14588                           
14589                             {
14590                                 xns : Roo.bootstrap,
14591                                 xtype : 'Button',
14592                                 style: 'max-height: 28px; ',
14593                                 size : 'sm',
14594                                 weight: 'danger',
14595                                 cls : 'col-2',
14596                                 fa : 'times',
14597                                 listeners : {
14598                                     click : function() {
14599                                         t.removeCard(data.id)
14600                                     }
14601                                 }
14602                             }
14603                         ]
14604                     }
14605                     
14606                 ] 
14607             }
14608             
14609         ];
14610         
14611         var cn = this.addxtype(
14612             {
14613                  
14614                 xns : Roo.bootstrap,
14615                 xtype : 'Card',
14616                 closeable : true,
14617                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14618                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14619                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14620                 data : data,
14621                 html : false,
14622                  
14623                 items : footer,
14624                 initEvents : function() {
14625                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14626                     var card = this;
14627                     this.imgEl = this.el.select('.card-img-top').first();
14628                     if (this.imgEl) {
14629                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14630                         this.imgEl.set({ 'pointer' : 'cursor' });
14631                                   
14632                     }
14633                     this.getCardFooter().addClass('p-1');
14634                     
14635                   
14636                 }
14637                 
14638             }
14639         );
14640         // dont' really need ot update items.
14641         // this.items.push(cn);
14642         this.fileCollection.add(cn);
14643         
14644         if (!data.srcfile) {
14645             this.updateInput();
14646             return;
14647         }
14648             
14649         var _t = this;
14650         var reader = new FileReader();
14651         reader.addEventListener("load", function() {  
14652             data.srcdata =  reader.result;
14653             _t.updateInput();
14654         });
14655         reader.readAsDataURL(data.srcfile);
14656         
14657         
14658         
14659     },
14660     removeCard : function(id)
14661     {
14662         
14663         var card  = this.fileCollection.get(id);
14664         card.data.is_deleted = 1;
14665         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14666         //this.fileCollection.remove(card);
14667         //this.items = this.items.filter(function(e) { return e != card });
14668         // dont' really need ot update items.
14669         card.el.dom.parentNode.removeChild(card.el.dom);
14670         this.updateInput();
14671
14672         
14673     },
14674     reset: function()
14675     {
14676         this.fileCollection.each(function(card) {
14677             if (card.el.dom && card.el.dom.parentNode) {
14678                 card.el.dom.parentNode.removeChild(card.el.dom);
14679             }
14680         });
14681         this.fileCollection.clear();
14682         this.updateInput();
14683     },
14684     
14685     updateInput : function()
14686     {
14687          var data = [];
14688         this.fileCollection.each(function(e) {
14689             data.push(e.data);
14690             
14691         });
14692         this.inputEl().dom.value = JSON.stringify(data);
14693         
14694         
14695         
14696     }
14697     
14698     
14699 });
14700
14701
14702 Roo.bootstrap.form.CardUploader.ID = -1;/*
14703  * Based on:
14704  * Ext JS Library 1.1.1
14705  * Copyright(c) 2006-2007, Ext JS, LLC.
14706  *
14707  * Originally Released Under LGPL - original licence link has changed is not relivant.
14708  *
14709  * Fork - LGPL
14710  * <script type="text/javascript">
14711  */
14712
14713
14714 /**
14715  * @class Roo.data.SortTypes
14716  * @static
14717  * Defines the default sorting (casting?) comparison functions used when sorting data.
14718  */
14719 Roo.data.SortTypes = {
14720     /**
14721      * Default sort that does nothing
14722      * @param {Mixed} s The value being converted
14723      * @return {Mixed} The comparison value
14724      */
14725     none : function(s){
14726         return s;
14727     },
14728     
14729     /**
14730      * The regular expression used to strip tags
14731      * @type {RegExp}
14732      * @property
14733      */
14734     stripTagsRE : /<\/?[^>]+>/gi,
14735     
14736     /**
14737      * Strips all HTML tags to sort on text only
14738      * @param {Mixed} s The value being converted
14739      * @return {String} The comparison value
14740      */
14741     asText : function(s){
14742         return String(s).replace(this.stripTagsRE, "");
14743     },
14744     
14745     /**
14746      * Strips all HTML tags to sort on text only - Case insensitive
14747      * @param {Mixed} s The value being converted
14748      * @return {String} The comparison value
14749      */
14750     asUCText : function(s){
14751         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14752     },
14753     
14754     /**
14755      * Case insensitive string
14756      * @param {Mixed} s The value being converted
14757      * @return {String} The comparison value
14758      */
14759     asUCString : function(s) {
14760         return String(s).toUpperCase();
14761     },
14762     
14763     /**
14764      * Date sorting
14765      * @param {Mixed} s The value being converted
14766      * @return {Number} The comparison value
14767      */
14768     asDate : function(s) {
14769         if(!s){
14770             return 0;
14771         }
14772         if(s instanceof Date){
14773             return s.getTime();
14774         }
14775         return Date.parse(String(s));
14776     },
14777     
14778     /**
14779      * Float sorting
14780      * @param {Mixed} s The value being converted
14781      * @return {Float} The comparison value
14782      */
14783     asFloat : function(s) {
14784         var val = parseFloat(String(s).replace(/,/g, ""));
14785         if(isNaN(val)) {
14786             val = 0;
14787         }
14788         return val;
14789     },
14790     
14791     /**
14792      * Integer sorting
14793      * @param {Mixed} s The value being converted
14794      * @return {Number} The comparison value
14795      */
14796     asInt : function(s) {
14797         var val = parseInt(String(s).replace(/,/g, ""));
14798         if(isNaN(val)) {
14799             val = 0;
14800         }
14801         return val;
14802     }
14803 };/*
14804  * Based on:
14805  * Ext JS Library 1.1.1
14806  * Copyright(c) 2006-2007, Ext JS, LLC.
14807  *
14808  * Originally Released Under LGPL - original licence link has changed is not relivant.
14809  *
14810  * Fork - LGPL
14811  * <script type="text/javascript">
14812  */
14813
14814 /**
14815 * @class Roo.data.Record
14816  * Instances of this class encapsulate both record <em>definition</em> information, and record
14817  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14818  * to access Records cached in an {@link Roo.data.Store} object.<br>
14819  * <p>
14820  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14821  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14822  * objects.<br>
14823  * <p>
14824  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14825  * @constructor
14826  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14827  * {@link #create}. The parameters are the same.
14828  * @param {Array} data An associative Array of data values keyed by the field name.
14829  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14830  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14831  * not specified an integer id is generated.
14832  */
14833 Roo.data.Record = function(data, id){
14834     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14835     this.data = data;
14836 };
14837
14838 /**
14839  * Generate a constructor for a specific record layout.
14840  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14841  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14842  * Each field definition object may contain the following properties: <ul>
14843  * <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,
14844  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14845  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14846  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14847  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14848  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14849  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14850  * this may be omitted.</p></li>
14851  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14852  * <ul><li>auto (Default, implies no conversion)</li>
14853  * <li>string</li>
14854  * <li>int</li>
14855  * <li>float</li>
14856  * <li>boolean</li>
14857  * <li>date</li></ul></p></li>
14858  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14859  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14860  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14861  * by the Reader into an object that will be stored in the Record. It is passed the
14862  * following parameters:<ul>
14863  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14864  * </ul></p></li>
14865  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14866  * </ul>
14867  * <br>usage:<br><pre><code>
14868 var TopicRecord = Roo.data.Record.create(
14869     {name: 'title', mapping: 'topic_title'},
14870     {name: 'author', mapping: 'username'},
14871     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14872     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14873     {name: 'lastPoster', mapping: 'user2'},
14874     {name: 'excerpt', mapping: 'post_text'}
14875 );
14876
14877 var myNewRecord = new TopicRecord({
14878     title: 'Do my job please',
14879     author: 'noobie',
14880     totalPosts: 1,
14881     lastPost: new Date(),
14882     lastPoster: 'Animal',
14883     excerpt: 'No way dude!'
14884 });
14885 myStore.add(myNewRecord);
14886 </code></pre>
14887  * @method create
14888  * @static
14889  */
14890 Roo.data.Record.create = function(o){
14891     var f = function(){
14892         f.superclass.constructor.apply(this, arguments);
14893     };
14894     Roo.extend(f, Roo.data.Record);
14895     var p = f.prototype;
14896     p.fields = new Roo.util.MixedCollection(false, function(field){
14897         return field.name;
14898     });
14899     for(var i = 0, len = o.length; i < len; i++){
14900         p.fields.add(new Roo.data.Field(o[i]));
14901     }
14902     f.getField = function(name){
14903         return p.fields.get(name);  
14904     };
14905     return f;
14906 };
14907
14908 Roo.data.Record.AUTO_ID = 1000;
14909 Roo.data.Record.EDIT = 'edit';
14910 Roo.data.Record.REJECT = 'reject';
14911 Roo.data.Record.COMMIT = 'commit';
14912
14913 Roo.data.Record.prototype = {
14914     /**
14915      * Readonly flag - true if this record has been modified.
14916      * @type Boolean
14917      */
14918     dirty : false,
14919     editing : false,
14920     error: null,
14921     modified: null,
14922
14923     // private
14924     join : function(store){
14925         this.store = store;
14926     },
14927
14928     /**
14929      * Set the named field to the specified value.
14930      * @param {String} name The name of the field to set.
14931      * @param {Object} value The value to set the field to.
14932      */
14933     set : function(name, value){
14934         if(this.data[name] == value){
14935             return;
14936         }
14937         this.dirty = true;
14938         if(!this.modified){
14939             this.modified = {};
14940         }
14941         if(typeof this.modified[name] == 'undefined'){
14942             this.modified[name] = this.data[name];
14943         }
14944         this.data[name] = value;
14945         if(!this.editing && this.store){
14946             this.store.afterEdit(this);
14947         }       
14948     },
14949
14950     /**
14951      * Get the value of the named field.
14952      * @param {String} name The name of the field to get the value of.
14953      * @return {Object} The value of the field.
14954      */
14955     get : function(name){
14956         return this.data[name]; 
14957     },
14958
14959     // private
14960     beginEdit : function(){
14961         this.editing = true;
14962         this.modified = {}; 
14963     },
14964
14965     // private
14966     cancelEdit : function(){
14967         this.editing = false;
14968         delete this.modified;
14969     },
14970
14971     // private
14972     endEdit : function(){
14973         this.editing = false;
14974         if(this.dirty && this.store){
14975             this.store.afterEdit(this);
14976         }
14977     },
14978
14979     /**
14980      * Usually called by the {@link Roo.data.Store} which owns the Record.
14981      * Rejects all changes made to the Record since either creation, or the last commit operation.
14982      * Modified fields are reverted to their original values.
14983      * <p>
14984      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14985      * of reject operations.
14986      */
14987     reject : function(){
14988         var m = this.modified;
14989         for(var n in m){
14990             if(typeof m[n] != "function"){
14991                 this.data[n] = m[n];
14992             }
14993         }
14994         this.dirty = false;
14995         delete this.modified;
14996         this.editing = false;
14997         if(this.store){
14998             this.store.afterReject(this);
14999         }
15000     },
15001
15002     /**
15003      * Usually called by the {@link Roo.data.Store} which owns the Record.
15004      * Commits all changes made to the Record since either creation, or the last commit operation.
15005      * <p>
15006      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15007      * of commit operations.
15008      */
15009     commit : function(){
15010         this.dirty = false;
15011         delete this.modified;
15012         this.editing = false;
15013         if(this.store){
15014             this.store.afterCommit(this);
15015         }
15016     },
15017
15018     // private
15019     hasError : function(){
15020         return this.error != null;
15021     },
15022
15023     // private
15024     clearError : function(){
15025         this.error = null;
15026     },
15027
15028     /**
15029      * Creates a copy of this record.
15030      * @param {String} id (optional) A new record id if you don't want to use this record's id
15031      * @return {Record}
15032      */
15033     copy : function(newId) {
15034         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15035     }
15036 };/*
15037  * Based on:
15038  * Ext JS Library 1.1.1
15039  * Copyright(c) 2006-2007, Ext JS, LLC.
15040  *
15041  * Originally Released Under LGPL - original licence link has changed is not relivant.
15042  *
15043  * Fork - LGPL
15044  * <script type="text/javascript">
15045  */
15046
15047
15048
15049 /**
15050  * @class Roo.data.Store
15051  * @extends Roo.util.Observable
15052  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15053  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15054  * <p>
15055  * 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
15056  * has no knowledge of the format of the data returned by the Proxy.<br>
15057  * <p>
15058  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15059  * instances from the data object. These records are cached and made available through accessor functions.
15060  * @constructor
15061  * Creates a new Store.
15062  * @param {Object} config A config object containing the objects needed for the Store to access data,
15063  * and read the data into Records.
15064  */
15065 Roo.data.Store = function(config){
15066     this.data = new Roo.util.MixedCollection(false);
15067     this.data.getKey = function(o){
15068         return o.id;
15069     };
15070     this.baseParams = {};
15071     // private
15072     this.paramNames = {
15073         "start" : "start",
15074         "limit" : "limit",
15075         "sort" : "sort",
15076         "dir" : "dir",
15077         "multisort" : "_multisort"
15078     };
15079
15080     if(config && config.data){
15081         this.inlineData = config.data;
15082         delete config.data;
15083     }
15084
15085     Roo.apply(this, config);
15086     
15087     if(this.reader){ // reader passed
15088         this.reader = Roo.factory(this.reader, Roo.data);
15089         this.reader.xmodule = this.xmodule || false;
15090         if(!this.recordType){
15091             this.recordType = this.reader.recordType;
15092         }
15093         if(this.reader.onMetaChange){
15094             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15095         }
15096     }
15097
15098     if(this.recordType){
15099         this.fields = this.recordType.prototype.fields;
15100     }
15101     this.modified = [];
15102
15103     this.addEvents({
15104         /**
15105          * @event datachanged
15106          * Fires when the data cache has changed, and a widget which is using this Store
15107          * as a Record cache should refresh its view.
15108          * @param {Store} this
15109          */
15110         datachanged : true,
15111         /**
15112          * @event metachange
15113          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15114          * @param {Store} this
15115          * @param {Object} meta The JSON metadata
15116          */
15117         metachange : true,
15118         /**
15119          * @event add
15120          * Fires when Records have been added to the Store
15121          * @param {Store} this
15122          * @param {Roo.data.Record[]} records The array of Records added
15123          * @param {Number} index The index at which the record(s) were added
15124          */
15125         add : true,
15126         /**
15127          * @event remove
15128          * Fires when a Record has been removed from the Store
15129          * @param {Store} this
15130          * @param {Roo.data.Record} record The Record that was removed
15131          * @param {Number} index The index at which the record was removed
15132          */
15133         remove : true,
15134         /**
15135          * @event update
15136          * Fires when a Record has been updated
15137          * @param {Store} this
15138          * @param {Roo.data.Record} record The Record that was updated
15139          * @param {String} operation The update operation being performed.  Value may be one of:
15140          * <pre><code>
15141  Roo.data.Record.EDIT
15142  Roo.data.Record.REJECT
15143  Roo.data.Record.COMMIT
15144          * </code></pre>
15145          */
15146         update : true,
15147         /**
15148          * @event clear
15149          * Fires when the data cache has been cleared.
15150          * @param {Store} this
15151          */
15152         clear : true,
15153         /**
15154          * @event beforeload
15155          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15156          * the load action will be canceled.
15157          * @param {Store} this
15158          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15159          */
15160         beforeload : true,
15161         /**
15162          * @event beforeloadadd
15163          * Fires after a new set of Records has been loaded.
15164          * @param {Store} this
15165          * @param {Roo.data.Record[]} records The Records that were loaded
15166          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15167          */
15168         beforeloadadd : true,
15169         /**
15170          * @event load
15171          * Fires after a new set of Records has been loaded, before they are added to the store.
15172          * @param {Store} this
15173          * @param {Roo.data.Record[]} records The Records that were loaded
15174          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15175          * @params {Object} return from reader
15176          */
15177         load : true,
15178         /**
15179          * @event loadexception
15180          * Fires if an exception occurs in the Proxy during loading.
15181          * Called with the signature of the Proxy's "loadexception" event.
15182          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15183          * 
15184          * @param {Proxy} 
15185          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15186          * @param {Object} load options 
15187          * @param {Object} jsonData from your request (normally this contains the Exception)
15188          */
15189         loadexception : true
15190     });
15191     
15192     if(this.proxy){
15193         this.proxy = Roo.factory(this.proxy, Roo.data);
15194         this.proxy.xmodule = this.xmodule || false;
15195         this.relayEvents(this.proxy,  ["loadexception"]);
15196     }
15197     this.sortToggle = {};
15198     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15199
15200     Roo.data.Store.superclass.constructor.call(this);
15201
15202     if(this.inlineData){
15203         this.loadData(this.inlineData);
15204         delete this.inlineData;
15205     }
15206 };
15207
15208 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15209      /**
15210     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15211     * without a remote query - used by combo/forms at present.
15212     */
15213     
15214     /**
15215     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15216     */
15217     /**
15218     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15219     */
15220     /**
15221     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15222     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15223     */
15224     /**
15225     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15226     * on any HTTP request
15227     */
15228     /**
15229     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15230     */
15231     /**
15232     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15233     */
15234     multiSort: false,
15235     /**
15236     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15237     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15238     */
15239     remoteSort : false,
15240
15241     /**
15242     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15243      * loaded or when a record is removed. (defaults to false).
15244     */
15245     pruneModifiedRecords : false,
15246
15247     // private
15248     lastOptions : null,
15249
15250     /**
15251      * Add Records to the Store and fires the add event.
15252      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15253      */
15254     add : function(records){
15255         records = [].concat(records);
15256         for(var i = 0, len = records.length; i < len; i++){
15257             records[i].join(this);
15258         }
15259         var index = this.data.length;
15260         this.data.addAll(records);
15261         this.fireEvent("add", this, records, index);
15262     },
15263
15264     /**
15265      * Remove a Record from the Store and fires the remove event.
15266      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15267      */
15268     remove : function(record){
15269         var index = this.data.indexOf(record);
15270         this.data.removeAt(index);
15271  
15272         if(this.pruneModifiedRecords){
15273             this.modified.remove(record);
15274         }
15275         this.fireEvent("remove", this, record, index);
15276     },
15277
15278     /**
15279      * Remove all Records from the Store and fires the clear event.
15280      */
15281     removeAll : function(){
15282         this.data.clear();
15283         if(this.pruneModifiedRecords){
15284             this.modified = [];
15285         }
15286         this.fireEvent("clear", this);
15287     },
15288
15289     /**
15290      * Inserts Records to the Store at the given index and fires the add event.
15291      * @param {Number} index The start index at which to insert the passed Records.
15292      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15293      */
15294     insert : function(index, records){
15295         records = [].concat(records);
15296         for(var i = 0, len = records.length; i < len; i++){
15297             this.data.insert(index, records[i]);
15298             records[i].join(this);
15299         }
15300         this.fireEvent("add", this, records, index);
15301     },
15302
15303     /**
15304      * Get the index within the cache of the passed Record.
15305      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15306      * @return {Number} The index of the passed Record. Returns -1 if not found.
15307      */
15308     indexOf : function(record){
15309         return this.data.indexOf(record);
15310     },
15311
15312     /**
15313      * Get the index within the cache of the Record with the passed id.
15314      * @param {String} id The id of the Record to find.
15315      * @return {Number} The index of the Record. Returns -1 if not found.
15316      */
15317     indexOfId : function(id){
15318         return this.data.indexOfKey(id);
15319     },
15320
15321     /**
15322      * Get the Record with the specified id.
15323      * @param {String} id The id of the Record to find.
15324      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15325      */
15326     getById : function(id){
15327         return this.data.key(id);
15328     },
15329
15330     /**
15331      * Get the Record at the specified index.
15332      * @param {Number} index The index of the Record to find.
15333      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15334      */
15335     getAt : function(index){
15336         return this.data.itemAt(index);
15337     },
15338
15339     /**
15340      * Returns a range of Records between specified indices.
15341      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15342      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15343      * @return {Roo.data.Record[]} An array of Records
15344      */
15345     getRange : function(start, end){
15346         return this.data.getRange(start, end);
15347     },
15348
15349     // private
15350     storeOptions : function(o){
15351         o = Roo.apply({}, o);
15352         delete o.callback;
15353         delete o.scope;
15354         this.lastOptions = o;
15355     },
15356
15357     /**
15358      * Loads the Record cache from the configured Proxy using the configured Reader.
15359      * <p>
15360      * If using remote paging, then the first load call must specify the <em>start</em>
15361      * and <em>limit</em> properties in the options.params property to establish the initial
15362      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15363      * <p>
15364      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15365      * and this call will return before the new data has been loaded. Perform any post-processing
15366      * in a callback function, or in a "load" event handler.</strong>
15367      * <p>
15368      * @param {Object} options An object containing properties which control loading options:<ul>
15369      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15370      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15371      * <pre>
15372                 {
15373                     data : data,  // array of key=>value data like JsonReader
15374                     total : data.length,
15375                     success : true
15376                     
15377                 }
15378         </pre>
15379             }.</li>
15380      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15381      * passed the following arguments:<ul>
15382      * <li>r : Roo.data.Record[]</li>
15383      * <li>options: Options object from the load call</li>
15384      * <li>success: Boolean success indicator</li></ul></li>
15385      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15386      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15387      * </ul>
15388      */
15389     load : function(options){
15390         options = options || {};
15391         if(this.fireEvent("beforeload", this, options) !== false){
15392             this.storeOptions(options);
15393             var p = Roo.apply(options.params || {}, this.baseParams);
15394             // if meta was not loaded from remote source.. try requesting it.
15395             if (!this.reader.metaFromRemote) {
15396                 p._requestMeta = 1;
15397             }
15398             if(this.sortInfo && this.remoteSort){
15399                 var pn = this.paramNames;
15400                 p[pn["sort"]] = this.sortInfo.field;
15401                 p[pn["dir"]] = this.sortInfo.direction;
15402             }
15403             if (this.multiSort) {
15404                 var pn = this.paramNames;
15405                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15406             }
15407             
15408             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15409         }
15410     },
15411
15412     /**
15413      * Reloads the Record cache from the configured Proxy using the configured Reader and
15414      * the options from the last load operation performed.
15415      * @param {Object} options (optional) An object containing properties which may override the options
15416      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15417      * the most recently used options are reused).
15418      */
15419     reload : function(options){
15420         this.load(Roo.applyIf(options||{}, this.lastOptions));
15421     },
15422
15423     // private
15424     // Called as a callback by the Reader during a load operation.
15425     loadRecords : function(o, options, success){
15426          
15427         if(!o){
15428             if(success !== false){
15429                 this.fireEvent("load", this, [], options, o);
15430             }
15431             if(options.callback){
15432                 options.callback.call(options.scope || this, [], options, false);
15433             }
15434             return;
15435         }
15436         // if data returned failure - throw an exception.
15437         if (o.success === false) {
15438             // show a message if no listener is registered.
15439             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15440                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15441             }
15442             // loadmask wil be hooked into this..
15443             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15444             return;
15445         }
15446         var r = o.records, t = o.totalRecords || r.length;
15447         
15448         this.fireEvent("beforeloadadd", this, r, options, o);
15449         
15450         if(!options || options.add !== true){
15451             if(this.pruneModifiedRecords){
15452                 this.modified = [];
15453             }
15454             for(var i = 0, len = r.length; i < len; i++){
15455                 r[i].join(this);
15456             }
15457             if(this.snapshot){
15458                 this.data = this.snapshot;
15459                 delete this.snapshot;
15460             }
15461             this.data.clear();
15462             this.data.addAll(r);
15463             this.totalLength = t;
15464             this.applySort();
15465             this.fireEvent("datachanged", this);
15466         }else{
15467             this.totalLength = Math.max(t, this.data.length+r.length);
15468             this.add(r);
15469         }
15470         
15471         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15472                 
15473             var e = new Roo.data.Record({});
15474
15475             e.set(this.parent.displayField, this.parent.emptyTitle);
15476             e.set(this.parent.valueField, '');
15477
15478             this.insert(0, e);
15479         }
15480             
15481         this.fireEvent("load", this, r, options, o);
15482         if(options.callback){
15483             options.callback.call(options.scope || this, r, options, true);
15484         }
15485     },
15486
15487
15488     /**
15489      * Loads data from a passed data block. A Reader which understands the format of the data
15490      * must have been configured in the constructor.
15491      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15492      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15493      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15494      */
15495     loadData : function(o, append){
15496         var r = this.reader.readRecords(o);
15497         this.loadRecords(r, {add: append}, true);
15498     },
15499     
15500      /**
15501      * using 'cn' the nested child reader read the child array into it's child stores.
15502      * @param {Object} rec The record with a 'children array
15503      */
15504     loadDataFromChildren : function(rec)
15505     {
15506         this.loadData(this.reader.toLoadData(rec));
15507     },
15508     
15509
15510     /**
15511      * Gets the number of cached records.
15512      * <p>
15513      * <em>If using paging, this may not be the total size of the dataset. If the data object
15514      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15515      * the data set size</em>
15516      */
15517     getCount : function(){
15518         return this.data.length || 0;
15519     },
15520
15521     /**
15522      * Gets the total number of records in the dataset as returned by the server.
15523      * <p>
15524      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15525      * the dataset size</em>
15526      */
15527     getTotalCount : function(){
15528         return this.totalLength || 0;
15529     },
15530
15531     /**
15532      * Returns the sort state of the Store as an object with two properties:
15533      * <pre><code>
15534  field {String} The name of the field by which the Records are sorted
15535  direction {String} The sort order, "ASC" or "DESC"
15536      * </code></pre>
15537      */
15538     getSortState : function(){
15539         return this.sortInfo;
15540     },
15541
15542     // private
15543     applySort : function(){
15544         if(this.sortInfo && !this.remoteSort){
15545             var s = this.sortInfo, f = s.field;
15546             var st = this.fields.get(f).sortType;
15547             var fn = function(r1, r2){
15548                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15549                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15550             };
15551             this.data.sort(s.direction, fn);
15552             if(this.snapshot && this.snapshot != this.data){
15553                 this.snapshot.sort(s.direction, fn);
15554             }
15555         }
15556     },
15557
15558     /**
15559      * Sets the default sort column and order to be used by the next load operation.
15560      * @param {String} fieldName The name of the field to sort by.
15561      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15562      */
15563     setDefaultSort : function(field, dir){
15564         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15565     },
15566
15567     /**
15568      * Sort the Records.
15569      * If remote sorting is used, the sort is performed on the server, and the cache is
15570      * reloaded. If local sorting is used, the cache is sorted internally.
15571      * @param {String} fieldName The name of the field to sort by.
15572      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15573      */
15574     sort : function(fieldName, dir){
15575         var f = this.fields.get(fieldName);
15576         if(!dir){
15577             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15578             
15579             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15580                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15581             }else{
15582                 dir = f.sortDir;
15583             }
15584         }
15585         this.sortToggle[f.name] = dir;
15586         this.sortInfo = {field: f.name, direction: dir};
15587         if(!this.remoteSort){
15588             this.applySort();
15589             this.fireEvent("datachanged", this);
15590         }else{
15591             this.load(this.lastOptions);
15592         }
15593     },
15594
15595     /**
15596      * Calls the specified function for each of the Records in the cache.
15597      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15598      * Returning <em>false</em> aborts and exits the iteration.
15599      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15600      */
15601     each : function(fn, scope){
15602         this.data.each(fn, scope);
15603     },
15604
15605     /**
15606      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15607      * (e.g., during paging).
15608      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15609      */
15610     getModifiedRecords : function(){
15611         return this.modified;
15612     },
15613
15614     // private
15615     createFilterFn : function(property, value, anyMatch){
15616         if(!value.exec){ // not a regex
15617             value = String(value);
15618             if(value.length == 0){
15619                 return false;
15620             }
15621             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15622         }
15623         return function(r){
15624             return value.test(r.data[property]);
15625         };
15626     },
15627
15628     /**
15629      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15630      * @param {String} property A field on your records
15631      * @param {Number} start The record index to start at (defaults to 0)
15632      * @param {Number} end The last record index to include (defaults to length - 1)
15633      * @return {Number} The sum
15634      */
15635     sum : function(property, start, end){
15636         var rs = this.data.items, v = 0;
15637         start = start || 0;
15638         end = (end || end === 0) ? end : rs.length-1;
15639
15640         for(var i = start; i <= end; i++){
15641             v += (rs[i].data[property] || 0);
15642         }
15643         return v;
15644     },
15645
15646     /**
15647      * Filter the records by a specified property.
15648      * @param {String} field A field on your records
15649      * @param {String/RegExp} value Either a string that the field
15650      * should start with or a RegExp to test against the field
15651      * @param {Boolean} anyMatch True to match any part not just the beginning
15652      */
15653     filter : function(property, value, anyMatch){
15654         var fn = this.createFilterFn(property, value, anyMatch);
15655         return fn ? this.filterBy(fn) : this.clearFilter();
15656     },
15657
15658     /**
15659      * Filter by a function. The specified function will be called with each
15660      * record in this data source. If the function returns true the record is included,
15661      * otherwise it is filtered.
15662      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15663      * @param {Object} scope (optional) The scope of the function (defaults to this)
15664      */
15665     filterBy : function(fn, scope){
15666         this.snapshot = this.snapshot || this.data;
15667         this.data = this.queryBy(fn, scope||this);
15668         this.fireEvent("datachanged", this);
15669     },
15670
15671     /**
15672      * Query the records by a specified property.
15673      * @param {String} field A field on your records
15674      * @param {String/RegExp} value Either a string that the field
15675      * should start with or a RegExp to test against the field
15676      * @param {Boolean} anyMatch True to match any part not just the beginning
15677      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15678      */
15679     query : function(property, value, anyMatch){
15680         var fn = this.createFilterFn(property, value, anyMatch);
15681         return fn ? this.queryBy(fn) : this.data.clone();
15682     },
15683
15684     /**
15685      * Query by a function. The specified function will be called with each
15686      * record in this data source. If the function returns true the record is included
15687      * in the results.
15688      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15689      * @param {Object} scope (optional) The scope of the function (defaults to this)
15690       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15691      **/
15692     queryBy : function(fn, scope){
15693         var data = this.snapshot || this.data;
15694         return data.filterBy(fn, scope||this);
15695     },
15696
15697     /**
15698      * Collects unique values for a particular dataIndex from this store.
15699      * @param {String} dataIndex The property to collect
15700      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15701      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15702      * @return {Array} An array of the unique values
15703      **/
15704     collect : function(dataIndex, allowNull, bypassFilter){
15705         var d = (bypassFilter === true && this.snapshot) ?
15706                 this.snapshot.items : this.data.items;
15707         var v, sv, r = [], l = {};
15708         for(var i = 0, len = d.length; i < len; i++){
15709             v = d[i].data[dataIndex];
15710             sv = String(v);
15711             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15712                 l[sv] = true;
15713                 r[r.length] = v;
15714             }
15715         }
15716         return r;
15717     },
15718
15719     /**
15720      * Revert to a view of the Record cache with no filtering applied.
15721      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15722      */
15723     clearFilter : function(suppressEvent){
15724         if(this.snapshot && this.snapshot != this.data){
15725             this.data = this.snapshot;
15726             delete this.snapshot;
15727             if(suppressEvent !== true){
15728                 this.fireEvent("datachanged", this);
15729             }
15730         }
15731     },
15732
15733     // private
15734     afterEdit : function(record){
15735         if(this.modified.indexOf(record) == -1){
15736             this.modified.push(record);
15737         }
15738         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15739     },
15740     
15741     // private
15742     afterReject : function(record){
15743         this.modified.remove(record);
15744         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15745     },
15746
15747     // private
15748     afterCommit : function(record){
15749         this.modified.remove(record);
15750         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15751     },
15752
15753     /**
15754      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15755      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15756      */
15757     commitChanges : function(){
15758         var m = this.modified.slice(0);
15759         this.modified = [];
15760         for(var i = 0, len = m.length; i < len; i++){
15761             m[i].commit();
15762         }
15763     },
15764
15765     /**
15766      * Cancel outstanding changes on all changed records.
15767      */
15768     rejectChanges : function(){
15769         var m = this.modified.slice(0);
15770         this.modified = [];
15771         for(var i = 0, len = m.length; i < len; i++){
15772             m[i].reject();
15773         }
15774     },
15775
15776     onMetaChange : function(meta, rtype, o){
15777         this.recordType = rtype;
15778         this.fields = rtype.prototype.fields;
15779         delete this.snapshot;
15780         this.sortInfo = meta.sortInfo || this.sortInfo;
15781         this.modified = [];
15782         this.fireEvent('metachange', this, this.reader.meta);
15783     },
15784     
15785     moveIndex : function(data, type)
15786     {
15787         var index = this.indexOf(data);
15788         
15789         var newIndex = index + type;
15790         
15791         this.remove(data);
15792         
15793         this.insert(newIndex, data);
15794         
15795     }
15796 });/*
15797  * Based on:
15798  * Ext JS Library 1.1.1
15799  * Copyright(c) 2006-2007, Ext JS, LLC.
15800  *
15801  * Originally Released Under LGPL - original licence link has changed is not relivant.
15802  *
15803  * Fork - LGPL
15804  * <script type="text/javascript">
15805  */
15806
15807 /**
15808  * @class Roo.data.SimpleStore
15809  * @extends Roo.data.Store
15810  * Small helper class to make creating Stores from Array data easier.
15811  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15812  * @cfg {Array} fields An array of field definition objects, or field name strings.
15813  * @cfg {Object} an existing reader (eg. copied from another store)
15814  * @cfg {Array} data The multi-dimensional array of data
15815  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15816  * @cfg {Roo.data.Reader} reader  [not-required] 
15817  * @constructor
15818  * @param {Object} config
15819  */
15820 Roo.data.SimpleStore = function(config)
15821 {
15822     Roo.data.SimpleStore.superclass.constructor.call(this, {
15823         isLocal : true,
15824         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15825                 id: config.id
15826             },
15827             Roo.data.Record.create(config.fields)
15828         ),
15829         proxy : new Roo.data.MemoryProxy(config.data)
15830     });
15831     this.load();
15832 };
15833 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15834  * Based on:
15835  * Ext JS Library 1.1.1
15836  * Copyright(c) 2006-2007, Ext JS, LLC.
15837  *
15838  * Originally Released Under LGPL - original licence link has changed is not relivant.
15839  *
15840  * Fork - LGPL
15841  * <script type="text/javascript">
15842  */
15843
15844 /**
15845 /**
15846  * @extends Roo.data.Store
15847  * @class Roo.data.JsonStore
15848  * Small helper class to make creating Stores for JSON data easier. <br/>
15849 <pre><code>
15850 var store = new Roo.data.JsonStore({
15851     url: 'get-images.php',
15852     root: 'images',
15853     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15854 });
15855 </code></pre>
15856  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15857  * JsonReader and HttpProxy (unless inline data is provided).</b>
15858  * @cfg {Array} fields An array of field definition objects, or field name strings.
15859  * @constructor
15860  * @param {Object} config
15861  */
15862 Roo.data.JsonStore = function(c){
15863     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15864         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15865         reader: new Roo.data.JsonReader(c, c.fields)
15866     }));
15867 };
15868 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15869  * Based on:
15870  * Ext JS Library 1.1.1
15871  * Copyright(c) 2006-2007, Ext JS, LLC.
15872  *
15873  * Originally Released Under LGPL - original licence link has changed is not relivant.
15874  *
15875  * Fork - LGPL
15876  * <script type="text/javascript">
15877  */
15878
15879  
15880 Roo.data.Field = function(config){
15881     if(typeof config == "string"){
15882         config = {name: config};
15883     }
15884     Roo.apply(this, config);
15885     
15886     if(!this.type){
15887         this.type = "auto";
15888     }
15889     
15890     var st = Roo.data.SortTypes;
15891     // named sortTypes are supported, here we look them up
15892     if(typeof this.sortType == "string"){
15893         this.sortType = st[this.sortType];
15894     }
15895     
15896     // set default sortType for strings and dates
15897     if(!this.sortType){
15898         switch(this.type){
15899             case "string":
15900                 this.sortType = st.asUCString;
15901                 break;
15902             case "date":
15903                 this.sortType = st.asDate;
15904                 break;
15905             default:
15906                 this.sortType = st.none;
15907         }
15908     }
15909
15910     // define once
15911     var stripRe = /[\$,%]/g;
15912
15913     // prebuilt conversion function for this field, instead of
15914     // switching every time we're reading a value
15915     if(!this.convert){
15916         var cv, dateFormat = this.dateFormat;
15917         switch(this.type){
15918             case "":
15919             case "auto":
15920             case undefined:
15921                 cv = function(v){ return v; };
15922                 break;
15923             case "string":
15924                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15925                 break;
15926             case "int":
15927                 cv = function(v){
15928                     return v !== undefined && v !== null && v !== '' ?
15929                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15930                     };
15931                 break;
15932             case "float":
15933                 cv = function(v){
15934                     return v !== undefined && v !== null && v !== '' ?
15935                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15936                     };
15937                 break;
15938             case "bool":
15939             case "boolean":
15940                 cv = function(v){ return v === true || v === "true" || v == 1; };
15941                 break;
15942             case "date":
15943                 cv = function(v){
15944                     if(!v){
15945                         return '';
15946                     }
15947                     if(v instanceof Date){
15948                         return v;
15949                     }
15950                     if(dateFormat){
15951                         if(dateFormat == "timestamp"){
15952                             return new Date(v*1000);
15953                         }
15954                         return Date.parseDate(v, dateFormat);
15955                     }
15956                     var parsed = Date.parse(v);
15957                     return parsed ? new Date(parsed) : null;
15958                 };
15959              break;
15960             
15961         }
15962         this.convert = cv;
15963     }
15964 };
15965
15966 Roo.data.Field.prototype = {
15967     dateFormat: null,
15968     defaultValue: "",
15969     mapping: null,
15970     sortType : null,
15971     sortDir : "ASC"
15972 };/*
15973  * Based on:
15974  * Ext JS Library 1.1.1
15975  * Copyright(c) 2006-2007, Ext JS, LLC.
15976  *
15977  * Originally Released Under LGPL - original licence link has changed is not relivant.
15978  *
15979  * Fork - LGPL
15980  * <script type="text/javascript">
15981  */
15982  
15983 // Base class for reading structured data from a data source.  This class is intended to be
15984 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15985
15986 /**
15987  * @class Roo.data.DataReader
15988  * @abstract
15989  * Base class for reading structured data from a data source.  This class is intended to be
15990  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15991  */
15992
15993 Roo.data.DataReader = function(meta, recordType){
15994     
15995     this.meta = meta;
15996     
15997     this.recordType = recordType instanceof Array ? 
15998         Roo.data.Record.create(recordType) : recordType;
15999 };
16000
16001 Roo.data.DataReader.prototype = {
16002     
16003     
16004     readerType : 'Data',
16005      /**
16006      * Create an empty record
16007      * @param {Object} data (optional) - overlay some values
16008      * @return {Roo.data.Record} record created.
16009      */
16010     newRow :  function(d) {
16011         var da =  {};
16012         this.recordType.prototype.fields.each(function(c) {
16013             switch( c.type) {
16014                 case 'int' : da[c.name] = 0; break;
16015                 case 'date' : da[c.name] = new Date(); break;
16016                 case 'float' : da[c.name] = 0.0; break;
16017                 case 'boolean' : da[c.name] = false; break;
16018                 default : da[c.name] = ""; break;
16019             }
16020             
16021         });
16022         return new this.recordType(Roo.apply(da, d));
16023     }
16024     
16025     
16026 };/*
16027  * Based on:
16028  * Ext JS Library 1.1.1
16029  * Copyright(c) 2006-2007, Ext JS, LLC.
16030  *
16031  * Originally Released Under LGPL - original licence link has changed is not relivant.
16032  *
16033  * Fork - LGPL
16034  * <script type="text/javascript">
16035  */
16036
16037 /**
16038  * @class Roo.data.DataProxy
16039  * @extends Roo.util.Observable
16040  * @abstract
16041  * This class is an abstract base class for implementations which provide retrieval of
16042  * unformatted data objects.<br>
16043  * <p>
16044  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16045  * (of the appropriate type which knows how to parse the data object) to provide a block of
16046  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16047  * <p>
16048  * Custom implementations must implement the load method as described in
16049  * {@link Roo.data.HttpProxy#load}.
16050  */
16051 Roo.data.DataProxy = function(){
16052     this.addEvents({
16053         /**
16054          * @event beforeload
16055          * Fires before a network request is made to retrieve a data object.
16056          * @param {Object} This DataProxy object.
16057          * @param {Object} params The params parameter to the load function.
16058          */
16059         beforeload : true,
16060         /**
16061          * @event load
16062          * Fires before the load method's callback is called.
16063          * @param {Object} This DataProxy object.
16064          * @param {Object} o The data object.
16065          * @param {Object} arg The callback argument object passed to the load function.
16066          */
16067         load : true,
16068         /**
16069          * @event loadexception
16070          * Fires if an Exception occurs during data retrieval.
16071          * @param {Object} This DataProxy object.
16072          * @param {Object} o The data object.
16073          * @param {Object} arg The callback argument object passed to the load function.
16074          * @param {Object} e The Exception.
16075          */
16076         loadexception : true
16077     });
16078     Roo.data.DataProxy.superclass.constructor.call(this);
16079 };
16080
16081 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16082
16083     /**
16084      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16085      */
16086 /*
16087  * Based on:
16088  * Ext JS Library 1.1.1
16089  * Copyright(c) 2006-2007, Ext JS, LLC.
16090  *
16091  * Originally Released Under LGPL - original licence link has changed is not relivant.
16092  *
16093  * Fork - LGPL
16094  * <script type="text/javascript">
16095  */
16096 /**
16097  * @class Roo.data.MemoryProxy
16098  * @extends Roo.data.DataProxy
16099  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16100  * to the Reader when its load method is called.
16101  * @constructor
16102  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16103  */
16104 Roo.data.MemoryProxy = function(config){
16105     var data = config;
16106     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16107         data = config.data;
16108     }
16109     Roo.data.MemoryProxy.superclass.constructor.call(this);
16110     this.data = data;
16111 };
16112
16113 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16114     
16115     /**
16116      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16117      */
16118     /**
16119      * Load data from the requested source (in this case an in-memory
16120      * data object passed to the constructor), read the data object into
16121      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16122      * process that block using the passed callback.
16123      * @param {Object} params This parameter is not used by the MemoryProxy class.
16124      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16125      * object into a block of Roo.data.Records.
16126      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16127      * The function must be passed <ul>
16128      * <li>The Record block object</li>
16129      * <li>The "arg" argument from the load function</li>
16130      * <li>A boolean success indicator</li>
16131      * </ul>
16132      * @param {Object} scope The scope in which to call the callback
16133      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16134      */
16135     load : function(params, reader, callback, scope, arg){
16136         params = params || {};
16137         var result;
16138         try {
16139             result = reader.readRecords(params.data ? params.data :this.data);
16140         }catch(e){
16141             this.fireEvent("loadexception", this, arg, null, e);
16142             callback.call(scope, null, arg, false);
16143             return;
16144         }
16145         callback.call(scope, result, arg, true);
16146     },
16147     
16148     // private
16149     update : function(params, records){
16150         
16151     }
16152 });/*
16153  * Based on:
16154  * Ext JS Library 1.1.1
16155  * Copyright(c) 2006-2007, Ext JS, LLC.
16156  *
16157  * Originally Released Under LGPL - original licence link has changed is not relivant.
16158  *
16159  * Fork - LGPL
16160  * <script type="text/javascript">
16161  */
16162 /**
16163  * @class Roo.data.HttpProxy
16164  * @extends Roo.data.DataProxy
16165  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16166  * configured to reference a certain URL.<br><br>
16167  * <p>
16168  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16169  * from which the running page was served.<br><br>
16170  * <p>
16171  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16172  * <p>
16173  * Be aware that to enable the browser to parse an XML document, the server must set
16174  * the Content-Type header in the HTTP response to "text/xml".
16175  * @constructor
16176  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16177  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16178  * will be used to make the request.
16179  */
16180 Roo.data.HttpProxy = function(conn){
16181     Roo.data.HttpProxy.superclass.constructor.call(this);
16182     // is conn a conn config or a real conn?
16183     this.conn = conn;
16184     this.useAjax = !conn || !conn.events;
16185   
16186 };
16187
16188 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16189     // thse are take from connection...
16190     
16191     /**
16192      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16193      */
16194     /**
16195      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16196      * extra parameters to each request made by this object. (defaults to undefined)
16197      */
16198     /**
16199      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16200      *  to each request made by this object. (defaults to undefined)
16201      */
16202     /**
16203      * @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)
16204      */
16205     /**
16206      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16207      */
16208      /**
16209      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16210      * @type Boolean
16211      */
16212   
16213
16214     /**
16215      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16216      * @type Boolean
16217      */
16218     /**
16219      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16220      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16221      * a finer-grained basis than the DataProxy events.
16222      */
16223     getConnection : function(){
16224         return this.useAjax ? Roo.Ajax : this.conn;
16225     },
16226
16227     /**
16228      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16229      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16230      * process that block using the passed callback.
16231      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16232      * for the request to the remote server.
16233      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16234      * object into a block of Roo.data.Records.
16235      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16236      * The function must be passed <ul>
16237      * <li>The Record block object</li>
16238      * <li>The "arg" argument from the load function</li>
16239      * <li>A boolean success indicator</li>
16240      * </ul>
16241      * @param {Object} scope The scope in which to call the callback
16242      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16243      */
16244     load : function(params, reader, callback, scope, arg){
16245         if(this.fireEvent("beforeload", this, params) !== false){
16246             var  o = {
16247                 params : params || {},
16248                 request: {
16249                     callback : callback,
16250                     scope : scope,
16251                     arg : arg
16252                 },
16253                 reader: reader,
16254                 callback : this.loadResponse,
16255                 scope: this
16256             };
16257             if(this.useAjax){
16258                 Roo.applyIf(o, this.conn);
16259                 if(this.activeRequest){
16260                     Roo.Ajax.abort(this.activeRequest);
16261                 }
16262                 this.activeRequest = Roo.Ajax.request(o);
16263             }else{
16264                 this.conn.request(o);
16265             }
16266         }else{
16267             callback.call(scope||this, null, arg, false);
16268         }
16269     },
16270
16271     // private
16272     loadResponse : function(o, success, response){
16273         delete this.activeRequest;
16274         if(!success){
16275             this.fireEvent("loadexception", this, o, response);
16276             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16277             return;
16278         }
16279         var result;
16280         try {
16281             result = o.reader.read(response);
16282         }catch(e){
16283             o.success = false;
16284             o.raw = { errorMsg : response.responseText };
16285             this.fireEvent("loadexception", this, o, response, e);
16286             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16287             return;
16288         }
16289         
16290         this.fireEvent("load", this, o, o.request.arg);
16291         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16292     },
16293
16294     // private
16295     update : function(dataSet){
16296
16297     },
16298
16299     // private
16300     updateResponse : function(dataSet){
16301
16302     }
16303 });/*
16304  * Based on:
16305  * Ext JS Library 1.1.1
16306  * Copyright(c) 2006-2007, Ext JS, LLC.
16307  *
16308  * Originally Released Under LGPL - original licence link has changed is not relivant.
16309  *
16310  * Fork - LGPL
16311  * <script type="text/javascript">
16312  */
16313
16314 /**
16315  * @class Roo.data.ScriptTagProxy
16316  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16317  * other than the originating domain of the running page.<br><br>
16318  * <p>
16319  * <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
16320  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16321  * <p>
16322  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16323  * source code that is used as the source inside a &lt;script> tag.<br><br>
16324  * <p>
16325  * In order for the browser to process the returned data, the server must wrap the data object
16326  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16327  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16328  * depending on whether the callback name was passed:
16329  * <p>
16330  * <pre><code>
16331 boolean scriptTag = false;
16332 String cb = request.getParameter("callback");
16333 if (cb != null) {
16334     scriptTag = true;
16335     response.setContentType("text/javascript");
16336 } else {
16337     response.setContentType("application/x-json");
16338 }
16339 Writer out = response.getWriter();
16340 if (scriptTag) {
16341     out.write(cb + "(");
16342 }
16343 out.print(dataBlock.toJsonString());
16344 if (scriptTag) {
16345     out.write(");");
16346 }
16347 </pre></code>
16348  *
16349  * @constructor
16350  * @param {Object} config A configuration object.
16351  */
16352 Roo.data.ScriptTagProxy = function(config){
16353     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16354     Roo.apply(this, config);
16355     this.head = document.getElementsByTagName("head")[0];
16356 };
16357
16358 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16359
16360 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16361     /**
16362      * @cfg {String} url The URL from which to request the data object.
16363      */
16364     /**
16365      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16366      */
16367     timeout : 30000,
16368     /**
16369      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16370      * the server the name of the callback function set up by the load call to process the returned data object.
16371      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16372      * javascript output which calls this named function passing the data object as its only parameter.
16373      */
16374     callbackParam : "callback",
16375     /**
16376      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16377      * name to the request.
16378      */
16379     nocache : true,
16380
16381     /**
16382      * Load data from the configured URL, read the data object into
16383      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16384      * process that block using the passed callback.
16385      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16386      * for the request to the remote server.
16387      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16388      * object into a block of Roo.data.Records.
16389      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16390      * The function must be passed <ul>
16391      * <li>The Record block object</li>
16392      * <li>The "arg" argument from the load function</li>
16393      * <li>A boolean success indicator</li>
16394      * </ul>
16395      * @param {Object} scope The scope in which to call the callback
16396      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16397      */
16398     load : function(params, reader, callback, scope, arg){
16399         if(this.fireEvent("beforeload", this, params) !== false){
16400
16401             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16402
16403             var url = this.url;
16404             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16405             if(this.nocache){
16406                 url += "&_dc=" + (new Date().getTime());
16407             }
16408             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16409             var trans = {
16410                 id : transId,
16411                 cb : "stcCallback"+transId,
16412                 scriptId : "stcScript"+transId,
16413                 params : params,
16414                 arg : arg,
16415                 url : url,
16416                 callback : callback,
16417                 scope : scope,
16418                 reader : reader
16419             };
16420             var conn = this;
16421
16422             window[trans.cb] = function(o){
16423                 conn.handleResponse(o, trans);
16424             };
16425
16426             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16427
16428             if(this.autoAbort !== false){
16429                 this.abort();
16430             }
16431
16432             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16433
16434             var script = document.createElement("script");
16435             script.setAttribute("src", url);
16436             script.setAttribute("type", "text/javascript");
16437             script.setAttribute("id", trans.scriptId);
16438             this.head.appendChild(script);
16439
16440             this.trans = trans;
16441         }else{
16442             callback.call(scope||this, null, arg, false);
16443         }
16444     },
16445
16446     // private
16447     isLoading : function(){
16448         return this.trans ? true : false;
16449     },
16450
16451     /**
16452      * Abort the current server request.
16453      */
16454     abort : function(){
16455         if(this.isLoading()){
16456             this.destroyTrans(this.trans);
16457         }
16458     },
16459
16460     // private
16461     destroyTrans : function(trans, isLoaded){
16462         this.head.removeChild(document.getElementById(trans.scriptId));
16463         clearTimeout(trans.timeoutId);
16464         if(isLoaded){
16465             window[trans.cb] = undefined;
16466             try{
16467                 delete window[trans.cb];
16468             }catch(e){}
16469         }else{
16470             // if hasn't been loaded, wait for load to remove it to prevent script error
16471             window[trans.cb] = function(){
16472                 window[trans.cb] = undefined;
16473                 try{
16474                     delete window[trans.cb];
16475                 }catch(e){}
16476             };
16477         }
16478     },
16479
16480     // private
16481     handleResponse : function(o, trans){
16482         this.trans = false;
16483         this.destroyTrans(trans, true);
16484         var result;
16485         try {
16486             result = trans.reader.readRecords(o);
16487         }catch(e){
16488             this.fireEvent("loadexception", this, o, trans.arg, e);
16489             trans.callback.call(trans.scope||window, null, trans.arg, false);
16490             return;
16491         }
16492         this.fireEvent("load", this, o, trans.arg);
16493         trans.callback.call(trans.scope||window, result, trans.arg, true);
16494     },
16495
16496     // private
16497     handleFailure : function(trans){
16498         this.trans = false;
16499         this.destroyTrans(trans, false);
16500         this.fireEvent("loadexception", this, null, trans.arg);
16501         trans.callback.call(trans.scope||window, null, trans.arg, false);
16502     }
16503 });/*
16504  * Based on:
16505  * Ext JS Library 1.1.1
16506  * Copyright(c) 2006-2007, Ext JS, LLC.
16507  *
16508  * Originally Released Under LGPL - original licence link has changed is not relivant.
16509  *
16510  * Fork - LGPL
16511  * <script type="text/javascript">
16512  */
16513
16514 /**
16515  * @class Roo.data.JsonReader
16516  * @extends Roo.data.DataReader
16517  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16518  * based on mappings in a provided Roo.data.Record constructor.
16519  * 
16520  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16521  * in the reply previously. 
16522  * 
16523  * <p>
16524  * Example code:
16525  * <pre><code>
16526 var RecordDef = Roo.data.Record.create([
16527     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16528     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16529 ]);
16530 var myReader = new Roo.data.JsonReader({
16531     totalProperty: "results",    // The property which contains the total dataset size (optional)
16532     root: "rows",                // The property which contains an Array of row objects
16533     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16534 }, RecordDef);
16535 </code></pre>
16536  * <p>
16537  * This would consume a JSON file like this:
16538  * <pre><code>
16539 { 'results': 2, 'rows': [
16540     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16541     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16542 }
16543 </code></pre>
16544  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16545  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16546  * paged from the remote server.
16547  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16548  * @cfg {String} root name of the property which contains the Array of row objects.
16549  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16550  * @cfg {Array} fields Array of field definition objects
16551  * @constructor
16552  * Create a new JsonReader
16553  * @param {Object} meta Metadata configuration options
16554  * @param {Object} recordType Either an Array of field definition objects,
16555  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16556  */
16557 Roo.data.JsonReader = function(meta, recordType){
16558     
16559     meta = meta || {};
16560     // set some defaults:
16561     Roo.applyIf(meta, {
16562         totalProperty: 'total',
16563         successProperty : 'success',
16564         root : 'data',
16565         id : 'id'
16566     });
16567     
16568     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16569 };
16570 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16571     
16572     readerType : 'Json',
16573     
16574     /**
16575      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16576      * Used by Store query builder to append _requestMeta to params.
16577      * 
16578      */
16579     metaFromRemote : false,
16580     /**
16581      * This method is only used by a DataProxy which has retrieved data from a remote server.
16582      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16583      * @return {Object} data A data block which is used by an Roo.data.Store object as
16584      * a cache of Roo.data.Records.
16585      */
16586     read : function(response){
16587         var json = response.responseText;
16588        
16589         var o = /* eval:var:o */ eval("("+json+")");
16590         if(!o) {
16591             throw {message: "JsonReader.read: Json object not found"};
16592         }
16593         
16594         if(o.metaData){
16595             
16596             delete this.ef;
16597             this.metaFromRemote = true;
16598             this.meta = o.metaData;
16599             this.recordType = Roo.data.Record.create(o.metaData.fields);
16600             this.onMetaChange(this.meta, this.recordType, o);
16601         }
16602         return this.readRecords(o);
16603     },
16604
16605     // private function a store will implement
16606     onMetaChange : function(meta, recordType, o){
16607
16608     },
16609
16610     /**
16611          * @ignore
16612          */
16613     simpleAccess: function(obj, subsc) {
16614         return obj[subsc];
16615     },
16616
16617         /**
16618          * @ignore
16619          */
16620     getJsonAccessor: function(){
16621         var re = /[\[\.]/;
16622         return function(expr) {
16623             try {
16624                 return(re.test(expr))
16625                     ? new Function("obj", "return obj." + expr)
16626                     : function(obj){
16627                         return obj[expr];
16628                     };
16629             } catch(e){}
16630             return Roo.emptyFn;
16631         };
16632     }(),
16633
16634     /**
16635      * Create a data block containing Roo.data.Records from an XML document.
16636      * @param {Object} o An object which contains an Array of row objects in the property specified
16637      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16638      * which contains the total size of the dataset.
16639      * @return {Object} data A data block which is used by an Roo.data.Store object as
16640      * a cache of Roo.data.Records.
16641      */
16642     readRecords : function(o){
16643         /**
16644          * After any data loads, the raw JSON data is available for further custom processing.
16645          * @type Object
16646          */
16647         this.o = o;
16648         var s = this.meta, Record = this.recordType,
16649             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16650
16651 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16652         if (!this.ef) {
16653             if(s.totalProperty) {
16654                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16655                 }
16656                 if(s.successProperty) {
16657                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16658                 }
16659                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16660                 if (s.id) {
16661                         var g = this.getJsonAccessor(s.id);
16662                         this.getId = function(rec) {
16663                                 var r = g(rec);  
16664                                 return (r === undefined || r === "") ? null : r;
16665                         };
16666                 } else {
16667                         this.getId = function(){return null;};
16668                 }
16669             this.ef = [];
16670             for(var jj = 0; jj < fl; jj++){
16671                 f = fi[jj];
16672                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16673                 this.ef[jj] = this.getJsonAccessor(map);
16674             }
16675         }
16676
16677         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16678         if(s.totalProperty){
16679             var vt = parseInt(this.getTotal(o), 10);
16680             if(!isNaN(vt)){
16681                 totalRecords = vt;
16682             }
16683         }
16684         if(s.successProperty){
16685             var vs = this.getSuccess(o);
16686             if(vs === false || vs === 'false'){
16687                 success = false;
16688             }
16689         }
16690         var records = [];
16691         for(var i = 0; i < c; i++){
16692             var n = root[i];
16693             var values = {};
16694             var id = this.getId(n);
16695             for(var j = 0; j < fl; j++){
16696                 f = fi[j];
16697                                 var v = this.ef[j](n);
16698                                 if (!f.convert) {
16699                                         Roo.log('missing convert for ' + f.name);
16700                                         Roo.log(f);
16701                                         continue;
16702                                 }
16703                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16704             }
16705                         if (!Record) {
16706                                 return {
16707                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16708                                         success : false,
16709                                         records : [],
16710                                         totalRecords : 0
16711                                 };
16712                         }
16713             var record = new Record(values, id);
16714             record.json = n;
16715             records[i] = record;
16716         }
16717         return {
16718             raw : o,
16719             success : success,
16720             records : records,
16721             totalRecords : totalRecords
16722         };
16723     },
16724     // used when loading children.. @see loadDataFromChildren
16725     toLoadData: function(rec)
16726     {
16727         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16728         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16729         return { data : data, total : data.length };
16730         
16731     }
16732 });/*
16733  * Based on:
16734  * Ext JS Library 1.1.1
16735  * Copyright(c) 2006-2007, Ext JS, LLC.
16736  *
16737  * Originally Released Under LGPL - original licence link has changed is not relivant.
16738  *
16739  * Fork - LGPL
16740  * <script type="text/javascript">
16741  */
16742
16743 /**
16744  * @class Roo.data.ArrayReader
16745  * @extends Roo.data.DataReader
16746  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16747  * Each element of that Array represents a row of data fields. The
16748  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16749  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16750  * <p>
16751  * Example code:.
16752  * <pre><code>
16753 var RecordDef = Roo.data.Record.create([
16754     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16755     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16756 ]);
16757 var myReader = new Roo.data.ArrayReader({
16758     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16759 }, RecordDef);
16760 </code></pre>
16761  * <p>
16762  * This would consume an Array like this:
16763  * <pre><code>
16764 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16765   </code></pre>
16766  
16767  * @constructor
16768  * Create a new JsonReader
16769  * @param {Object} meta Metadata configuration options.
16770  * @param {Object|Array} recordType Either an Array of field definition objects
16771  * 
16772  * @cfg {Array} fields Array of field definition objects
16773  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16774  * as specified to {@link Roo.data.Record#create},
16775  * or an {@link Roo.data.Record} object
16776  *
16777  * 
16778  * created using {@link Roo.data.Record#create}.
16779  */
16780 Roo.data.ArrayReader = function(meta, recordType)
16781 {    
16782     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16783 };
16784
16785 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16786     
16787       /**
16788      * Create a data block containing Roo.data.Records from an XML document.
16789      * @param {Object} o An Array of row objects which represents the dataset.
16790      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16791      * a cache of Roo.data.Records.
16792      */
16793     readRecords : function(o)
16794     {
16795         var sid = this.meta ? this.meta.id : null;
16796         var recordType = this.recordType, fields = recordType.prototype.fields;
16797         var records = [];
16798         var root = o;
16799         for(var i = 0; i < root.length; i++){
16800             var n = root[i];
16801             var values = {};
16802             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16803             for(var j = 0, jlen = fields.length; j < jlen; j++){
16804                 var f = fields.items[j];
16805                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16806                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16807                 v = f.convert(v);
16808                 values[f.name] = v;
16809             }
16810             var record = new recordType(values, id);
16811             record.json = n;
16812             records[records.length] = record;
16813         }
16814         return {
16815             records : records,
16816             totalRecords : records.length
16817         };
16818     },
16819     // used when loading children.. @see loadDataFromChildren
16820     toLoadData: function(rec)
16821     {
16822         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16823         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16824         
16825     }
16826     
16827     
16828 });/*
16829  * - LGPL
16830  * * 
16831  */
16832
16833 /**
16834  * @class Roo.bootstrap.form.ComboBox
16835  * @extends Roo.bootstrap.form.TriggerField
16836  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16837  * @cfg {Boolean} append (true|false) default false
16838  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16839  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16840  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16841  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16842  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16843  * @cfg {Boolean} animate default true
16844  * @cfg {Boolean} emptyResultText only for touch device
16845  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16846  * @cfg {String} emptyTitle default ''
16847  * @cfg {Number} width fixed with? experimental
16848  * @constructor
16849  * Create a new ComboBox.
16850  * @param {Object} config Configuration options
16851  */
16852 Roo.bootstrap.form.ComboBox = function(config){
16853     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16854     this.addEvents({
16855         /**
16856          * @event expand
16857          * Fires when the dropdown list is expanded
16858         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859         */
16860         'expand' : true,
16861         /**
16862          * @event collapse
16863          * Fires when the dropdown list is collapsed
16864         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16865         */
16866         'collapse' : true,
16867         /**
16868          * @event beforeselect
16869          * Fires before a list item is selected. Return false to cancel the selection.
16870         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871         * @param {Roo.data.Record} record The data record returned from the underlying store
16872         * @param {Number} index The index of the selected item in the dropdown list
16873         */
16874         'beforeselect' : true,
16875         /**
16876          * @event select
16877          * Fires when a list item is selected
16878         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16879         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16880         * @param {Number} index The index of the selected item in the dropdown list
16881         */
16882         'select' : true,
16883         /**
16884          * @event beforequery
16885          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16886          * The event object passed has these properties:
16887         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16888         * @param {String} query The query
16889         * @param {Boolean} forceAll true to force "all" query
16890         * @param {Boolean} cancel true to cancel the query
16891         * @param {Object} e The query event object
16892         */
16893         'beforequery': true,
16894          /**
16895          * @event add
16896          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16897         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16898         */
16899         'add' : true,
16900         /**
16901          * @event edit
16902          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16903         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16905         */
16906         'edit' : true,
16907         /**
16908          * @event remove
16909          * Fires when the remove value from the combobox array
16910         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16911         */
16912         'remove' : true,
16913         /**
16914          * @event afterremove
16915          * Fires when the remove value from the combobox array
16916         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16917         */
16918         'afterremove' : true,
16919         /**
16920          * @event specialfilter
16921          * Fires when specialfilter
16922             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16923             */
16924         'specialfilter' : true,
16925         /**
16926          * @event tick
16927          * Fires when tick the element
16928             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16929             */
16930         'tick' : true,
16931         /**
16932          * @event touchviewdisplay
16933          * Fires when touch view require special display (default is using displayField)
16934             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16935             * @param {Object} cfg set html .
16936             */
16937         'touchviewdisplay' : true
16938         
16939     });
16940     
16941     this.item = [];
16942     this.tickItems = [];
16943     
16944     this.selectedIndex = -1;
16945     if(this.mode == 'local'){
16946         if(config.queryDelay === undefined){
16947             this.queryDelay = 10;
16948         }
16949         if(config.minChars === undefined){
16950             this.minChars = 0;
16951         }
16952     }
16953 };
16954
16955 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16956      
16957     /**
16958      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16959      * rendering into an Roo.Editor, defaults to false)
16960      */
16961     /**
16962      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16963      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16964      */
16965     /**
16966      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16967      */
16968     /**
16969      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16970      * the dropdown list (defaults to undefined, with no header element)
16971      */
16972
16973      /**
16974      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16975      */
16976      
16977      /**
16978      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16979      */
16980     listWidth: undefined,
16981     /**
16982      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16983      * mode = 'remote' or 'text' if mode = 'local')
16984      */
16985     displayField: undefined,
16986     
16987     /**
16988      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16989      * mode = 'remote' or 'value' if mode = 'local'). 
16990      * Note: use of a valueField requires the user make a selection
16991      * in order for a value to be mapped.
16992      */
16993     valueField: undefined,
16994     /**
16995      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16996      */
16997     modalTitle : '',
16998     
16999     /**
17000      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17001      * field's data value (defaults to the underlying DOM element's name)
17002      */
17003     hiddenName: undefined,
17004     /**
17005      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17006      */
17007     listClass: '',
17008     /**
17009      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17010      */
17011     selectedClass: 'active',
17012     
17013     /**
17014      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17015      */
17016     shadow:'sides',
17017     /**
17018      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17019      * anchor positions (defaults to 'tl-bl')
17020      */
17021     listAlign: 'tl-bl?',
17022     /**
17023      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17024      */
17025     maxHeight: 300,
17026     /**
17027      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17028      * query specified by the allQuery config option (defaults to 'query')
17029      */
17030     triggerAction: 'query',
17031     /**
17032      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17033      * (defaults to 4, does not apply if editable = false)
17034      */
17035     minChars : 4,
17036     /**
17037      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17038      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17039      */
17040     typeAhead: false,
17041     /**
17042      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17043      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17044      */
17045     queryDelay: 500,
17046     /**
17047      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17048      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17049      */
17050     pageSize: 0,
17051     /**
17052      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17053      * when editable = true (defaults to false)
17054      */
17055     selectOnFocus:false,
17056     /**
17057      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17058      */
17059     queryParam: 'query',
17060     /**
17061      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17062      * when mode = 'remote' (defaults to 'Loading...')
17063      */
17064     loadingText: 'Loading...',
17065     /**
17066      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17067      */
17068     resizable: false,
17069     /**
17070      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17071      */
17072     handleHeight : 8,
17073     /**
17074      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17075      * traditional select (defaults to true)
17076      */
17077     editable: true,
17078     /**
17079      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17080      */
17081     allQuery: '',
17082     /**
17083      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17084      */
17085     mode: 'remote',
17086     /**
17087      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17088      * listWidth has a higher value)
17089      */
17090     minListWidth : 70,
17091     /**
17092      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17093      * allow the user to set arbitrary text into the field (defaults to false)
17094      */
17095     forceSelection:false,
17096     /**
17097      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17098      * if typeAhead = true (defaults to 250)
17099      */
17100     typeAheadDelay : 250,
17101     /**
17102      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17103      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17104      */
17105     valueNotFoundText : undefined,
17106     /**
17107      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17108      */
17109     blockFocus : false,
17110     
17111     /**
17112      * @cfg {Boolean} disableClear Disable showing of clear button.
17113      */
17114     disableClear : false,
17115     /**
17116      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17117      */
17118     alwaysQuery : false,
17119     
17120     /**
17121      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17122      */
17123     multiple : false,
17124     
17125     /**
17126      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17127      */
17128     invalidClass : "has-warning",
17129     
17130     /**
17131      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17132      */
17133     validClass : "has-success",
17134     
17135     /**
17136      * @cfg {Boolean} specialFilter (true|false) special filter default false
17137      */
17138     specialFilter : false,
17139     
17140     /**
17141      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17142      */
17143     mobileTouchView : true,
17144     
17145     /**
17146      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17147      */
17148     useNativeIOS : false,
17149     
17150     /**
17151      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17152      */
17153     mobile_restrict_height : false,
17154     
17155     ios_options : false,
17156     
17157     //private
17158     addicon : false,
17159     editicon: false,
17160     
17161     page: 0,
17162     hasQuery: false,
17163     append: false,
17164     loadNext: false,
17165     autoFocus : true,
17166     tickable : false,
17167     btnPosition : 'right',
17168     triggerList : true,
17169     showToggleBtn : true,
17170     animate : true,
17171     emptyResultText: 'Empty',
17172     triggerText : 'Select',
17173     emptyTitle : '',
17174     width : false,
17175     
17176     // element that contains real text value.. (when hidden is used..)
17177     
17178     getAutoCreate : function()
17179     {   
17180         var cfg = false;
17181         //render
17182         /*
17183          * Render classic select for iso
17184          */
17185         
17186         if(Roo.isIOS && this.useNativeIOS){
17187             cfg = this.getAutoCreateNativeIOS();
17188             return cfg;
17189         }
17190         
17191         /*
17192          * Touch Devices
17193          */
17194         
17195         if(Roo.isTouch && this.mobileTouchView){
17196             cfg = this.getAutoCreateTouchView();
17197             return cfg;;
17198         }
17199         
17200         /*
17201          *  Normal ComboBox
17202          */
17203         if(!this.tickable){
17204             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17205             return cfg;
17206         }
17207         
17208         /*
17209          *  ComboBox with tickable selections
17210          */
17211              
17212         var align = this.labelAlign || this.parentLabelAlign();
17213         
17214         cfg = {
17215             cls : 'form-group roo-combobox-tickable' //input-group
17216         };
17217         
17218         var btn_text_select = '';
17219         var btn_text_done = '';
17220         var btn_text_cancel = '';
17221         
17222         if (this.btn_text_show) {
17223             btn_text_select = 'Select';
17224             btn_text_done = 'Done';
17225             btn_text_cancel = 'Cancel'; 
17226         }
17227         
17228         var buttons = {
17229             tag : 'div',
17230             cls : 'tickable-buttons',
17231             cn : [
17232                 {
17233                     tag : 'button',
17234                     type : 'button',
17235                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17236                     //html : this.triggerText
17237                     html: btn_text_select
17238                 },
17239                 {
17240                     tag : 'button',
17241                     type : 'button',
17242                     name : 'ok',
17243                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17244                     //html : 'Done'
17245                     html: btn_text_done
17246                 },
17247                 {
17248                     tag : 'button',
17249                     type : 'button',
17250                     name : 'cancel',
17251                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17252                     //html : 'Cancel'
17253                     html: btn_text_cancel
17254                 }
17255             ]
17256         };
17257         
17258         if(this.editable){
17259             buttons.cn.unshift({
17260                 tag: 'input',
17261                 cls: 'roo-select2-search-field-input'
17262             });
17263         }
17264         
17265         var _this = this;
17266         
17267         Roo.each(buttons.cn, function(c){
17268             if (_this.size) {
17269                 c.cls += ' btn-' + _this.size;
17270             }
17271
17272             if (_this.disabled) {
17273                 c.disabled = true;
17274             }
17275         });
17276         
17277         var box = {
17278             tag: 'div',
17279             style : 'display: contents',
17280             cn: [
17281                 {
17282                     tag: 'input',
17283                     type : 'hidden',
17284                     cls: 'form-hidden-field'
17285                 },
17286                 {
17287                     tag: 'ul',
17288                     cls: 'roo-select2-choices',
17289                     cn:[
17290                         {
17291                             tag: 'li',
17292                             cls: 'roo-select2-search-field',
17293                             cn: [
17294                                 buttons
17295                             ]
17296                         }
17297                     ]
17298                 }
17299             ]
17300         };
17301         
17302         var combobox = {
17303             cls: 'roo-select2-container input-group roo-select2-container-multi',
17304             cn: [
17305                 
17306                 box
17307 //                {
17308 //                    tag: 'ul',
17309 //                    cls: 'typeahead typeahead-long dropdown-menu',
17310 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17311 //                }
17312             ]
17313         };
17314         
17315         if(this.hasFeedback && !this.allowBlank){
17316             
17317             var feedback = {
17318                 tag: 'span',
17319                 cls: 'glyphicon form-control-feedback'
17320             };
17321
17322             combobox.cn.push(feedback);
17323         }
17324         
17325         
17326         
17327         var indicator = {
17328             tag : 'i',
17329             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17330             tooltip : 'This field is required'
17331         };
17332         if (Roo.bootstrap.version == 4) {
17333             indicator = {
17334                 tag : 'i',
17335                 style : 'display:none'
17336             };
17337         }
17338         if (align ==='left' && this.fieldLabel.length) {
17339             
17340             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17341             
17342             cfg.cn = [
17343                 indicator,
17344                 {
17345                     tag: 'label',
17346                     'for' :  id,
17347                     cls : 'control-label col-form-label',
17348                     html : this.fieldLabel
17349
17350                 },
17351                 {
17352                     cls : "", 
17353                     cn: [
17354                         combobox
17355                     ]
17356                 }
17357
17358             ];
17359             
17360             var labelCfg = cfg.cn[1];
17361             var contentCfg = cfg.cn[2];
17362             
17363
17364             if(this.indicatorpos == 'right'){
17365                 
17366                 cfg.cn = [
17367                     {
17368                         tag: 'label',
17369                         'for' :  id,
17370                         cls : 'control-label col-form-label',
17371                         cn : [
17372                             {
17373                                 tag : 'span',
17374                                 html : this.fieldLabel
17375                             },
17376                             indicator
17377                         ]
17378                     },
17379                     {
17380                         cls : "",
17381                         cn: [
17382                             combobox
17383                         ]
17384                     }
17385
17386                 ];
17387                 
17388                 
17389                 
17390                 labelCfg = cfg.cn[0];
17391                 contentCfg = cfg.cn[1];
17392             
17393             }
17394             
17395             if(this.labelWidth > 12){
17396                 labelCfg.style = "width: " + this.labelWidth + 'px';
17397             }
17398             if(this.width * 1 > 0){
17399                 contentCfg.style = "width: " + this.width + 'px';
17400             }
17401             if(this.labelWidth < 13 && this.labelmd == 0){
17402                 this.labelmd = this.labelWidth;
17403             }
17404             
17405             if(this.labellg > 0){
17406                 labelCfg.cls += ' col-lg-' + this.labellg;
17407                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17408             }
17409             
17410             if(this.labelmd > 0){
17411                 labelCfg.cls += ' col-md-' + this.labelmd;
17412                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17413             }
17414             
17415             if(this.labelsm > 0){
17416                 labelCfg.cls += ' col-sm-' + this.labelsm;
17417                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17418             }
17419             
17420             if(this.labelxs > 0){
17421                 labelCfg.cls += ' col-xs-' + this.labelxs;
17422                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17423             }
17424                 
17425                 
17426         } else if ( this.fieldLabel.length) {
17427 //                Roo.log(" label");
17428                  cfg.cn = [
17429                    indicator,
17430                     {
17431                         tag: 'label',
17432                         //cls : 'input-group-addon',
17433                         html : this.fieldLabel
17434                     },
17435                     combobox
17436                 ];
17437                 
17438                 if(this.indicatorpos == 'right'){
17439                     cfg.cn = [
17440                         {
17441                             tag: 'label',
17442                             //cls : 'input-group-addon',
17443                             html : this.fieldLabel
17444                         },
17445                         indicator,
17446                         combobox
17447                     ];
17448                     
17449                 }
17450
17451         } else {
17452             
17453 //                Roo.log(" no label && no align");
17454                 cfg = combobox
17455                      
17456                 
17457         }
17458          
17459         var settings=this;
17460         ['xs','sm','md','lg'].map(function(size){
17461             if (settings[size]) {
17462                 cfg.cls += ' col-' + size + '-' + settings[size];
17463             }
17464         });
17465         
17466         return cfg;
17467         
17468     },
17469     
17470     _initEventsCalled : false,
17471     
17472     // private
17473     initEvents: function()
17474     {   
17475         if (this._initEventsCalled) { // as we call render... prevent looping...
17476             return;
17477         }
17478         this._initEventsCalled = true;
17479         
17480         if (!this.store) {
17481             throw "can not find store for combo";
17482         }
17483         
17484         this.indicator = this.indicatorEl();
17485         
17486         this.store = Roo.factory(this.store, Roo.data);
17487         this.store.parent = this;
17488         
17489         // if we are building from html. then this element is so complex, that we can not really
17490         // use the rendered HTML.
17491         // so we have to trash and replace the previous code.
17492         if (Roo.XComponent.build_from_html) {
17493             // remove this element....
17494             var e = this.el.dom, k=0;
17495             while (e ) { e = e.previousSibling;  ++k;}
17496
17497             this.el.remove();
17498             
17499             this.el=false;
17500             this.rendered = false;
17501             
17502             this.render(this.parent().getChildContainer(true), k);
17503         }
17504         
17505         if(Roo.isIOS && this.useNativeIOS){
17506             this.initIOSView();
17507             return;
17508         }
17509         
17510         /*
17511          * Touch Devices
17512          */
17513         
17514         if(Roo.isTouch && this.mobileTouchView){
17515             this.initTouchView();
17516             return;
17517         }
17518         
17519         if(this.tickable){
17520             this.initTickableEvents();
17521             return;
17522         }
17523         
17524         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17525         
17526         if(this.hiddenName){
17527             
17528             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17529             
17530             this.hiddenField.dom.value =
17531                 this.hiddenValue !== undefined ? this.hiddenValue :
17532                 this.value !== undefined ? this.value : '';
17533
17534             // prevent input submission
17535             this.el.dom.removeAttribute('name');
17536             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17537              
17538              
17539         }
17540         //if(Roo.isGecko){
17541         //    this.el.dom.setAttribute('autocomplete', 'off');
17542         //}
17543         
17544         var cls = 'x-combo-list';
17545         
17546         //this.list = new Roo.Layer({
17547         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17548         //});
17549         
17550         var _this = this;
17551         
17552         (function(){
17553             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17554             _this.list.setWidth(lw);
17555         }).defer(100);
17556         
17557         this.list.on('mouseover', this.onViewOver, this);
17558         this.list.on('mousemove', this.onViewMove, this);
17559         this.list.on('scroll', this.onViewScroll, this);
17560         
17561         /*
17562         this.list.swallowEvent('mousewheel');
17563         this.assetHeight = 0;
17564
17565         if(this.title){
17566             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17567             this.assetHeight += this.header.getHeight();
17568         }
17569
17570         this.innerList = this.list.createChild({cls:cls+'-inner'});
17571         this.innerList.on('mouseover', this.onViewOver, this);
17572         this.innerList.on('mousemove', this.onViewMove, this);
17573         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17574         
17575         if(this.allowBlank && !this.pageSize && !this.disableClear){
17576             this.footer = this.list.createChild({cls:cls+'-ft'});
17577             this.pageTb = new Roo.Toolbar(this.footer);
17578            
17579         }
17580         if(this.pageSize){
17581             this.footer = this.list.createChild({cls:cls+'-ft'});
17582             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17583                     {pageSize: this.pageSize});
17584             
17585         }
17586         
17587         if (this.pageTb && this.allowBlank && !this.disableClear) {
17588             var _this = this;
17589             this.pageTb.add(new Roo.Toolbar.Fill(), {
17590                 cls: 'x-btn-icon x-btn-clear',
17591                 text: '&#160;',
17592                 handler: function()
17593                 {
17594                     _this.collapse();
17595                     _this.clearValue();
17596                     _this.onSelect(false, -1);
17597                 }
17598             });
17599         }
17600         if (this.footer) {
17601             this.assetHeight += this.footer.getHeight();
17602         }
17603         */
17604             
17605         if(!this.tpl){
17606             this.tpl = Roo.bootstrap.version == 4 ?
17607                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17608                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17609         }
17610
17611         this.view = new Roo.View(this.list, this.tpl, {
17612             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17613         });
17614         //this.view.wrapEl.setDisplayed(false);
17615         this.view.on('click', this.onViewClick, this);
17616         
17617         
17618         this.store.on('beforeload', this.onBeforeLoad, this);
17619         this.store.on('load', this.onLoad, this);
17620         this.store.on('loadexception', this.onLoadException, this);
17621         /*
17622         if(this.resizable){
17623             this.resizer = new Roo.Resizable(this.list,  {
17624                pinned:true, handles:'se'
17625             });
17626             this.resizer.on('resize', function(r, w, h){
17627                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17628                 this.listWidth = w;
17629                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17630                 this.restrictHeight();
17631             }, this);
17632             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17633         }
17634         */
17635         if(!this.editable){
17636             this.editable = true;
17637             this.setEditable(false);
17638         }
17639         
17640         /*
17641         
17642         if (typeof(this.events.add.listeners) != 'undefined') {
17643             
17644             this.addicon = this.wrap.createChild(
17645                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17646        
17647             this.addicon.on('click', function(e) {
17648                 this.fireEvent('add', this);
17649             }, this);
17650         }
17651         if (typeof(this.events.edit.listeners) != 'undefined') {
17652             
17653             this.editicon = this.wrap.createChild(
17654                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17655             if (this.addicon) {
17656                 this.editicon.setStyle('margin-left', '40px');
17657             }
17658             this.editicon.on('click', function(e) {
17659                 
17660                 // we fire even  if inothing is selected..
17661                 this.fireEvent('edit', this, this.lastData );
17662                 
17663             }, this);
17664         }
17665         */
17666         
17667         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17668             "up" : function(e){
17669                 this.inKeyMode = true;
17670                 this.selectPrev();
17671             },
17672
17673             "down" : function(e){
17674                 if(!this.isExpanded()){
17675                     this.onTriggerClick();
17676                 }else{
17677                     this.inKeyMode = true;
17678                     this.selectNext();
17679                 }
17680             },
17681
17682             "enter" : function(e){
17683 //                this.onViewClick();
17684                 //return true;
17685                 this.collapse();
17686                 
17687                 if(this.fireEvent("specialkey", this, e)){
17688                     this.onViewClick(false);
17689                 }
17690                 
17691                 return true;
17692             },
17693
17694             "esc" : function(e){
17695                 this.collapse();
17696             },
17697
17698             "tab" : function(e){
17699                 this.collapse();
17700                 
17701                 if(this.fireEvent("specialkey", this, e)){
17702                     this.onViewClick(false);
17703                 }
17704                 
17705                 return true;
17706             },
17707
17708             scope : this,
17709
17710             doRelay : function(foo, bar, hname){
17711                 if(hname == 'down' || this.scope.isExpanded()){
17712                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17713                 }
17714                 return true;
17715             },
17716
17717             forceKeyDown: true
17718         });
17719         
17720         
17721         this.queryDelay = Math.max(this.queryDelay || 10,
17722                 this.mode == 'local' ? 10 : 250);
17723         
17724         
17725         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17726         
17727         if(this.typeAhead){
17728             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17729         }
17730         if(this.editable !== false){
17731             this.inputEl().on("keyup", this.onKeyUp, this);
17732         }
17733         if(this.forceSelection){
17734             this.inputEl().on('blur', this.doForce, this);
17735         }
17736         
17737         if(this.multiple){
17738             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17739             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17740         }
17741     },
17742     
17743     initTickableEvents: function()
17744     {   
17745         this.createList();
17746         
17747         if(this.hiddenName){
17748             
17749             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17750             
17751             this.hiddenField.dom.value =
17752                 this.hiddenValue !== undefined ? this.hiddenValue :
17753                 this.value !== undefined ? this.value : '';
17754
17755             // prevent input submission
17756             this.el.dom.removeAttribute('name');
17757             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17758              
17759              
17760         }
17761         
17762 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17763         
17764         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17765         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17766         if(this.triggerList){
17767             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17768         }
17769          
17770         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17771         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17772         
17773         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17774         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17775         
17776         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17777         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17778         
17779         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17780         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17781         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17782         
17783         this.okBtn.hide();
17784         this.cancelBtn.hide();
17785         
17786         var _this = this;
17787         
17788         (function(){
17789             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17790             _this.list.setWidth(lw);
17791         }).defer(100);
17792         
17793         this.list.on('mouseover', this.onViewOver, this);
17794         this.list.on('mousemove', this.onViewMove, this);
17795         
17796         this.list.on('scroll', this.onViewScroll, this);
17797         
17798         if(!this.tpl){
17799             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17800                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17801         }
17802
17803         this.view = new Roo.View(this.list, this.tpl, {
17804             singleSelect:true,
17805             tickable:true,
17806             parent:this,
17807             store: this.store,
17808             selectedClass: this.selectedClass
17809         });
17810         
17811         //this.view.wrapEl.setDisplayed(false);
17812         this.view.on('click', this.onViewClick, this);
17813         
17814         
17815         
17816         this.store.on('beforeload', this.onBeforeLoad, this);
17817         this.store.on('load', this.onLoad, this);
17818         this.store.on('loadexception', this.onLoadException, this);
17819         
17820         if(this.editable){
17821             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17822                 "up" : function(e){
17823                     this.inKeyMode = true;
17824                     this.selectPrev();
17825                 },
17826
17827                 "down" : function(e){
17828                     this.inKeyMode = true;
17829                     this.selectNext();
17830                 },
17831
17832                 "enter" : function(e){
17833                     if(this.fireEvent("specialkey", this, e)){
17834                         this.onViewClick(false);
17835                     }
17836                     
17837                     return true;
17838                 },
17839
17840                 "esc" : function(e){
17841                     this.onTickableFooterButtonClick(e, false, false);
17842                 },
17843
17844                 "tab" : function(e){
17845                     this.fireEvent("specialkey", this, e);
17846                     
17847                     this.onTickableFooterButtonClick(e, false, false);
17848                     
17849                     return true;
17850                 },
17851
17852                 scope : this,
17853
17854                 doRelay : function(e, fn, key){
17855                     if(this.scope.isExpanded()){
17856                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17857                     }
17858                     return true;
17859                 },
17860
17861                 forceKeyDown: true
17862             });
17863         }
17864         
17865         this.queryDelay = Math.max(this.queryDelay || 10,
17866                 this.mode == 'local' ? 10 : 250);
17867         
17868         
17869         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17870         
17871         if(this.typeAhead){
17872             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17873         }
17874         
17875         if(this.editable !== false){
17876             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17877         }
17878         
17879         this.indicator = this.indicatorEl();
17880         
17881         if(this.indicator){
17882             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17883             this.indicator.hide();
17884         }
17885         
17886     },
17887
17888     onDestroy : function(){
17889         if(this.view){
17890             this.view.setStore(null);
17891             this.view.el.removeAllListeners();
17892             this.view.el.remove();
17893             this.view.purgeListeners();
17894         }
17895         if(this.list){
17896             this.list.dom.innerHTML  = '';
17897         }
17898         
17899         if(this.store){
17900             this.store.un('beforeload', this.onBeforeLoad, this);
17901             this.store.un('load', this.onLoad, this);
17902             this.store.un('loadexception', this.onLoadException, this);
17903         }
17904         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17905     },
17906
17907     // private
17908     fireKey : function(e){
17909         if(e.isNavKeyPress() && !this.list.isVisible()){
17910             this.fireEvent("specialkey", this, e);
17911         }
17912     },
17913
17914     // private
17915     onResize: function(w, h)
17916     {
17917         
17918         
17919 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17920 //        
17921 //        if(typeof w != 'number'){
17922 //            // we do not handle it!?!?
17923 //            return;
17924 //        }
17925 //        var tw = this.trigger.getWidth();
17926 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17927 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17928 //        var x = w - tw;
17929 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17930 //            
17931 //        //this.trigger.setStyle('left', x+'px');
17932 //        
17933 //        if(this.list && this.listWidth === undefined){
17934 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17935 //            this.list.setWidth(lw);
17936 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17937 //        }
17938         
17939     
17940         
17941     },
17942
17943     /**
17944      * Allow or prevent the user from directly editing the field text.  If false is passed,
17945      * the user will only be able to select from the items defined in the dropdown list.  This method
17946      * is the runtime equivalent of setting the 'editable' config option at config time.
17947      * @param {Boolean} value True to allow the user to directly edit the field text
17948      */
17949     setEditable : function(value){
17950         if(value == this.editable){
17951             return;
17952         }
17953         this.editable = value;
17954         if(!value){
17955             this.inputEl().dom.setAttribute('readOnly', true);
17956             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17957             this.inputEl().addClass('x-combo-noedit');
17958         }else{
17959             this.inputEl().dom.removeAttribute('readOnly');
17960             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17961             this.inputEl().removeClass('x-combo-noedit');
17962         }
17963     },
17964
17965     // private
17966     
17967     onBeforeLoad : function(combo,opts){
17968         if(!this.hasFocus){
17969             return;
17970         }
17971          if (!opts.add) {
17972             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17973          }
17974         this.restrictHeight();
17975         this.selectedIndex = -1;
17976     },
17977
17978     // private
17979     onLoad : function(){
17980         
17981         this.hasQuery = false;
17982         
17983         if(!this.hasFocus){
17984             return;
17985         }
17986         
17987         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17988             this.loading.hide();
17989         }
17990         
17991         if(this.store.getCount() > 0){
17992             
17993             this.expand();
17994             this.restrictHeight();
17995             if(this.lastQuery == this.allQuery){
17996                 if(this.editable && !this.tickable){
17997                     this.inputEl().dom.select();
17998                 }
17999                 
18000                 if(
18001                     !this.selectByValue(this.value, true) &&
18002                     this.autoFocus && 
18003                     (
18004                         !this.store.lastOptions ||
18005                         typeof(this.store.lastOptions.add) == 'undefined' || 
18006                         this.store.lastOptions.add != true
18007                     )
18008                 ){
18009                     this.select(0, true);
18010                 }
18011             }else{
18012                 if(this.autoFocus){
18013                     this.selectNext();
18014                 }
18015                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18016                     this.taTask.delay(this.typeAheadDelay);
18017                 }
18018             }
18019         }else{
18020             this.onEmptyResults();
18021         }
18022         
18023         //this.el.focus();
18024     },
18025     // private
18026     onLoadException : function()
18027     {
18028         this.hasQuery = false;
18029         
18030         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18031             this.loading.hide();
18032         }
18033         
18034         if(this.tickable && this.editable){
18035             return;
18036         }
18037         
18038         this.collapse();
18039         // only causes errors at present
18040         //Roo.log(this.store.reader.jsonData);
18041         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18042             // fixme
18043             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18044         //}
18045         
18046         
18047     },
18048     // private
18049     onTypeAhead : function(){
18050         if(this.store.getCount() > 0){
18051             var r = this.store.getAt(0);
18052             var newValue = r.data[this.displayField];
18053             var len = newValue.length;
18054             var selStart = this.getRawValue().length;
18055             
18056             if(selStart != len){
18057                 this.setRawValue(newValue);
18058                 this.selectText(selStart, newValue.length);
18059             }
18060         }
18061     },
18062
18063     // private
18064     onSelect : function(record, index){
18065         
18066         if(this.fireEvent('beforeselect', this, record, index) !== false){
18067         
18068             this.setFromData(index > -1 ? record.data : false);
18069             
18070             this.collapse();
18071             this.fireEvent('select', this, record, index);
18072         }
18073     },
18074
18075     /**
18076      * Returns the currently selected field value or empty string if no value is set.
18077      * @return {String} value The selected value
18078      */
18079     getValue : function()
18080     {
18081         if(Roo.isIOS && this.useNativeIOS){
18082             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18083         }
18084         
18085         if(this.multiple){
18086             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18087         }
18088         
18089         if(this.valueField){
18090             return typeof this.value != 'undefined' ? this.value : '';
18091         }else{
18092             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18093         }
18094     },
18095     
18096     getRawValue : function()
18097     {
18098         if(Roo.isIOS && this.useNativeIOS){
18099             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18100         }
18101         
18102         var v = this.inputEl().getValue();
18103         
18104         return v;
18105     },
18106
18107     /**
18108      * Clears any text/value currently set in the field
18109      */
18110     clearValue : function(){
18111         
18112         if(this.hiddenField){
18113             this.hiddenField.dom.value = '';
18114         }
18115         this.value = '';
18116         this.setRawValue('');
18117         this.lastSelectionText = '';
18118         this.lastData = false;
18119         
18120         var close = this.closeTriggerEl();
18121         
18122         if(close){
18123             close.hide();
18124         }
18125         
18126         this.validate();
18127         
18128     },
18129
18130     /**
18131      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18132      * will be displayed in the field.  If the value does not match the data value of an existing item,
18133      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18134      * Otherwise the field will be blank (although the value will still be set).
18135      * @param {String} value The value to match
18136      */
18137     setValue : function(v)
18138     {
18139         if(Roo.isIOS && this.useNativeIOS){
18140             this.setIOSValue(v);
18141             return;
18142         }
18143         
18144         if(this.multiple){
18145             this.syncValue();
18146             return;
18147         }
18148         
18149         var text = v;
18150         if(this.valueField){
18151             var r = this.findRecord(this.valueField, v);
18152             if(r){
18153                 text = r.data[this.displayField];
18154             }else if(this.valueNotFoundText !== undefined){
18155                 text = this.valueNotFoundText;
18156             }
18157         }
18158         this.lastSelectionText = text;
18159         if(this.hiddenField){
18160             this.hiddenField.dom.value = v;
18161         }
18162         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18163         this.value = v;
18164         
18165         var close = this.closeTriggerEl();
18166         
18167         if(close){
18168             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18169         }
18170         
18171         this.validate();
18172     },
18173     /**
18174      * @property {Object} the last set data for the element
18175      */
18176     
18177     lastData : false,
18178     /**
18179      * Sets the value of the field based on a object which is related to the record format for the store.
18180      * @param {Object} value the value to set as. or false on reset?
18181      */
18182     setFromData : function(o){
18183         
18184         if(this.multiple){
18185             this.addItem(o);
18186             return;
18187         }
18188             
18189         var dv = ''; // display value
18190         var vv = ''; // value value..
18191         this.lastData = o;
18192         if (this.displayField) {
18193             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18194         } else {
18195             // this is an error condition!!!
18196             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18197         }
18198         
18199         if(this.valueField){
18200             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18201         }
18202         
18203         var close = this.closeTriggerEl();
18204         
18205         if(close){
18206             if(dv.length || vv * 1 > 0){
18207                 close.show() ;
18208                 this.blockFocus=true;
18209             } else {
18210                 close.hide();
18211             }             
18212         }
18213         
18214         if(this.hiddenField){
18215             this.hiddenField.dom.value = vv;
18216             
18217             this.lastSelectionText = dv;
18218             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18219             this.value = vv;
18220             return;
18221         }
18222         // no hidden field.. - we store the value in 'value', but still display
18223         // display field!!!!
18224         this.lastSelectionText = dv;
18225         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18226         this.value = vv;
18227         
18228         
18229         
18230     },
18231     // private
18232     reset : function(){
18233         // overridden so that last data is reset..
18234         
18235         if(this.multiple){
18236             this.clearItem();
18237             return;
18238         }
18239         
18240         this.setValue(this.originalValue);
18241         //this.clearInvalid();
18242         this.lastData = false;
18243         if (this.view) {
18244             this.view.clearSelections();
18245         }
18246         
18247         this.validate();
18248     },
18249     // private
18250     findRecord : function(prop, value){
18251         var record;
18252         if(this.store.getCount() > 0){
18253             this.store.each(function(r){
18254                 if(r.data[prop] == value){
18255                     record = r;
18256                     return false;
18257                 }
18258                 return true;
18259             });
18260         }
18261         return record;
18262     },
18263     
18264     getName: function()
18265     {
18266         // returns hidden if it's set..
18267         if (!this.rendered) {return ''};
18268         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18269         
18270     },
18271     // private
18272     onViewMove : function(e, t){
18273         this.inKeyMode = false;
18274     },
18275
18276     // private
18277     onViewOver : function(e, t){
18278         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18279             return;
18280         }
18281         var item = this.view.findItemFromChild(t);
18282         
18283         if(item){
18284             var index = this.view.indexOf(item);
18285             this.select(index, false);
18286         }
18287     },
18288
18289     // private
18290     onViewClick : function(view, doFocus, el, e)
18291     {
18292         var index = this.view.getSelectedIndexes()[0];
18293         
18294         var r = this.store.getAt(index);
18295         
18296         if(this.tickable){
18297             
18298             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18299                 return;
18300             }
18301             
18302             var rm = false;
18303             var _this = this;
18304             
18305             Roo.each(this.tickItems, function(v,k){
18306                 
18307                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18308                     Roo.log(v);
18309                     _this.tickItems.splice(k, 1);
18310                     
18311                     if(typeof(e) == 'undefined' && view == false){
18312                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18313                     }
18314                     
18315                     rm = true;
18316                     return;
18317                 }
18318             });
18319             
18320             if(rm){
18321                 return;
18322             }
18323             
18324             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18325                 this.tickItems.push(r.data);
18326             }
18327             
18328             if(typeof(e) == 'undefined' && view == false){
18329                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18330             }
18331                     
18332             return;
18333         }
18334         
18335         if(r){
18336             this.onSelect(r, index);
18337         }
18338         if(doFocus !== false && !this.blockFocus){
18339             this.inputEl().focus();
18340         }
18341     },
18342
18343     // private
18344     restrictHeight : function(){
18345         //this.innerList.dom.style.height = '';
18346         //var inner = this.innerList.dom;
18347         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18348         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18349         //this.list.beginUpdate();
18350         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18351         this.list.alignTo(this.inputEl(), this.listAlign);
18352         this.list.alignTo(this.inputEl(), this.listAlign);
18353         //this.list.endUpdate();
18354     },
18355
18356     // private
18357     onEmptyResults : function(){
18358         
18359         if(this.tickable && this.editable){
18360             this.hasFocus = false;
18361             this.restrictHeight();
18362             return;
18363         }
18364         
18365         this.collapse();
18366     },
18367
18368     /**
18369      * Returns true if the dropdown list is expanded, else false.
18370      */
18371     isExpanded : function(){
18372         return this.list.isVisible();
18373     },
18374
18375     /**
18376      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18377      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18378      * @param {String} value The data value of the item to select
18379      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18380      * selected item if it is not currently in view (defaults to true)
18381      * @return {Boolean} True if the value matched an item in the list, else false
18382      */
18383     selectByValue : function(v, scrollIntoView){
18384         if(v !== undefined && v !== null){
18385             var r = this.findRecord(this.valueField || this.displayField, v);
18386             if(r){
18387                 this.select(this.store.indexOf(r), scrollIntoView);
18388                 return true;
18389             }
18390         }
18391         return false;
18392     },
18393
18394     /**
18395      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18396      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18397      * @param {Number} index The zero-based index of the list item to select
18398      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18399      * selected item if it is not currently in view (defaults to true)
18400      */
18401     select : function(index, scrollIntoView){
18402         this.selectedIndex = index;
18403         this.view.select(index);
18404         if(scrollIntoView !== false){
18405             var el = this.view.getNode(index);
18406             /*
18407              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18408              */
18409             if(el){
18410                 this.list.scrollChildIntoView(el, false);
18411             }
18412         }
18413     },
18414
18415     // private
18416     selectNext : function(){
18417         var ct = this.store.getCount();
18418         if(ct > 0){
18419             if(this.selectedIndex == -1){
18420                 this.select(0);
18421             }else if(this.selectedIndex < ct-1){
18422                 this.select(this.selectedIndex+1);
18423             }
18424         }
18425     },
18426
18427     // private
18428     selectPrev : function(){
18429         var ct = this.store.getCount();
18430         if(ct > 0){
18431             if(this.selectedIndex == -1){
18432                 this.select(0);
18433             }else if(this.selectedIndex != 0){
18434                 this.select(this.selectedIndex-1);
18435             }
18436         }
18437     },
18438
18439     // private
18440     onKeyUp : function(e){
18441         if(this.editable !== false && !e.isSpecialKey()){
18442             this.lastKey = e.getKey();
18443             this.dqTask.delay(this.queryDelay);
18444         }
18445     },
18446
18447     // private
18448     validateBlur : function(){
18449         return !this.list || !this.list.isVisible();   
18450     },
18451
18452     // private
18453     initQuery : function(){
18454         
18455         var v = this.getRawValue();
18456         
18457         if(this.tickable && this.editable){
18458             v = this.tickableInputEl().getValue();
18459         }
18460         
18461         this.doQuery(v);
18462     },
18463
18464     // private
18465     doForce : function(){
18466         if(this.inputEl().dom.value.length > 0){
18467             this.inputEl().dom.value =
18468                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18469              
18470         }
18471     },
18472
18473     /**
18474      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18475      * query allowing the query action to be canceled if needed.
18476      * @param {String} query The SQL query to execute
18477      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18478      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18479      * saved in the current store (defaults to false)
18480      */
18481     doQuery : function(q, forceAll){
18482         
18483         if(q === undefined || q === null){
18484             q = '';
18485         }
18486         var qe = {
18487             query: q,
18488             forceAll: forceAll,
18489             combo: this,
18490             cancel:false
18491         };
18492         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18493             return false;
18494         }
18495         q = qe.query;
18496         
18497         forceAll = qe.forceAll;
18498         if(forceAll === true || (q.length >= this.minChars)){
18499             
18500             this.hasQuery = true;
18501             
18502             if(this.lastQuery != q || this.alwaysQuery){
18503                 this.lastQuery = q;
18504                 if(this.mode == 'local'){
18505                     this.selectedIndex = -1;
18506                     if(forceAll){
18507                         this.store.clearFilter();
18508                     }else{
18509                         
18510                         if(this.specialFilter){
18511                             this.fireEvent('specialfilter', this);
18512                             this.onLoad();
18513                             return;
18514                         }
18515                         
18516                         this.store.filter(this.displayField, q);
18517                     }
18518                     
18519                     this.store.fireEvent("datachanged", this.store);
18520                     
18521                     this.onLoad();
18522                     
18523                     
18524                 }else{
18525                     
18526                     this.store.baseParams[this.queryParam] = q;
18527                     
18528                     var options = {params : this.getParams(q)};
18529                     
18530                     if(this.loadNext){
18531                         options.add = true;
18532                         options.params.start = this.page * this.pageSize;
18533                     }
18534                     
18535                     this.store.load(options);
18536                     
18537                     /*
18538                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18539                      *  we should expand the list on onLoad
18540                      *  so command out it
18541                      */
18542 //                    this.expand();
18543                 }
18544             }else{
18545                 this.selectedIndex = -1;
18546                 this.onLoad();   
18547             }
18548         }
18549         
18550         this.loadNext = false;
18551     },
18552     
18553     // private
18554     getParams : function(q){
18555         var p = {};
18556         //p[this.queryParam] = q;
18557         
18558         if(this.pageSize){
18559             p.start = 0;
18560             p.limit = this.pageSize;
18561         }
18562         return p;
18563     },
18564
18565     /**
18566      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18567      */
18568     collapse : function(){
18569         if(!this.isExpanded()){
18570             return;
18571         }
18572         
18573         this.list.hide();
18574         
18575         this.hasFocus = false;
18576         
18577         if(this.tickable){
18578             this.okBtn.hide();
18579             this.cancelBtn.hide();
18580             this.trigger.show();
18581             
18582             if(this.editable){
18583                 this.tickableInputEl().dom.value = '';
18584                 this.tickableInputEl().blur();
18585             }
18586             
18587         }
18588         
18589         Roo.get(document).un('mousedown', this.collapseIf, this);
18590         Roo.get(document).un('mousewheel', this.collapseIf, this);
18591         if (!this.editable) {
18592             Roo.get(document).un('keydown', this.listKeyPress, this);
18593         }
18594         this.fireEvent('collapse', this);
18595         
18596         this.validate();
18597     },
18598
18599     // private
18600     collapseIf : function(e){
18601         var in_combo  = e.within(this.el);
18602         var in_list =  e.within(this.list);
18603         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18604         
18605         if (in_combo || in_list || is_list) {
18606             //e.stopPropagation();
18607             return;
18608         }
18609         
18610         if(this.tickable){
18611             this.onTickableFooterButtonClick(e, false, false);
18612         }
18613
18614         this.collapse();
18615         
18616     },
18617
18618     /**
18619      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18620      */
18621     expand : function(){
18622        
18623         if(this.isExpanded() || !this.hasFocus){
18624             return;
18625         }
18626         
18627         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18628         this.list.setWidth(lw);
18629         
18630         Roo.log('expand');
18631         
18632         this.list.show();
18633         
18634         this.restrictHeight();
18635         
18636         if(this.tickable){
18637             
18638             this.tickItems = Roo.apply([], this.item);
18639             
18640             this.okBtn.show();
18641             this.cancelBtn.show();
18642             this.trigger.hide();
18643             
18644             if(this.editable){
18645                 this.tickableInputEl().focus();
18646             }
18647             
18648         }
18649         
18650         Roo.get(document).on('mousedown', this.collapseIf, this);
18651         Roo.get(document).on('mousewheel', this.collapseIf, this);
18652         if (!this.editable) {
18653             Roo.get(document).on('keydown', this.listKeyPress, this);
18654         }
18655         
18656         this.fireEvent('expand', this);
18657     },
18658
18659     // private
18660     // Implements the default empty TriggerField.onTriggerClick function
18661     onTriggerClick : function(e)
18662     {
18663         Roo.log('trigger click');
18664         
18665         if(this.disabled || !this.triggerList){
18666             return;
18667         }
18668         
18669         this.page = 0;
18670         this.loadNext = false;
18671         
18672         if(this.isExpanded()){
18673             this.collapse();
18674             if (!this.blockFocus) {
18675                 this.inputEl().focus();
18676             }
18677             
18678         }else {
18679             this.hasFocus = true;
18680             if(this.triggerAction == 'all') {
18681                 this.doQuery(this.allQuery, true);
18682             } else {
18683                 this.doQuery(this.getRawValue());
18684             }
18685             if (!this.blockFocus) {
18686                 this.inputEl().focus();
18687             }
18688         }
18689     },
18690     
18691     onTickableTriggerClick : function(e)
18692     {
18693         if(this.disabled){
18694             return;
18695         }
18696         
18697         this.page = 0;
18698         this.loadNext = false;
18699         this.hasFocus = true;
18700         
18701         if(this.triggerAction == 'all') {
18702             this.doQuery(this.allQuery, true);
18703         } else {
18704             this.doQuery(this.getRawValue());
18705         }
18706     },
18707     
18708     onSearchFieldClick : function(e)
18709     {
18710         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18711             this.onTickableFooterButtonClick(e, false, false);
18712             return;
18713         }
18714         
18715         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18716             return;
18717         }
18718         
18719         this.page = 0;
18720         this.loadNext = false;
18721         this.hasFocus = true;
18722         
18723         if(this.triggerAction == 'all') {
18724             this.doQuery(this.allQuery, true);
18725         } else {
18726             this.doQuery(this.getRawValue());
18727         }
18728     },
18729     
18730     listKeyPress : function(e)
18731     {
18732         //Roo.log('listkeypress');
18733         // scroll to first matching element based on key pres..
18734         if (e.isSpecialKey()) {
18735             return false;
18736         }
18737         var k = String.fromCharCode(e.getKey()).toUpperCase();
18738         //Roo.log(k);
18739         var match  = false;
18740         var csel = this.view.getSelectedNodes();
18741         var cselitem = false;
18742         if (csel.length) {
18743             var ix = this.view.indexOf(csel[0]);
18744             cselitem  = this.store.getAt(ix);
18745             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18746                 cselitem = false;
18747             }
18748             
18749         }
18750         
18751         this.store.each(function(v) { 
18752             if (cselitem) {
18753                 // start at existing selection.
18754                 if (cselitem.id == v.id) {
18755                     cselitem = false;
18756                 }
18757                 return true;
18758             }
18759                 
18760             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18761                 match = this.store.indexOf(v);
18762                 return false;
18763             }
18764             return true;
18765         }, this);
18766         
18767         if (match === false) {
18768             return true; // no more action?
18769         }
18770         // scroll to?
18771         this.view.select(match);
18772         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18773         sn.scrollIntoView(sn.dom.parentNode, false);
18774     },
18775     
18776     onViewScroll : function(e, t){
18777         
18778         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){
18779             return;
18780         }
18781         
18782         this.hasQuery = true;
18783         
18784         this.loading = this.list.select('.loading', true).first();
18785         
18786         if(this.loading === null){
18787             this.list.createChild({
18788                 tag: 'div',
18789                 cls: 'loading roo-select2-more-results roo-select2-active',
18790                 html: 'Loading more results...'
18791             });
18792             
18793             this.loading = this.list.select('.loading', true).first();
18794             
18795             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18796             
18797             this.loading.hide();
18798         }
18799         
18800         this.loading.show();
18801         
18802         var _combo = this;
18803         
18804         this.page++;
18805         this.loadNext = true;
18806         
18807         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18808         
18809         return;
18810     },
18811     
18812     addItem : function(o)
18813     {   
18814         var dv = ''; // display value
18815         
18816         if (this.displayField) {
18817             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18818         } else {
18819             // this is an error condition!!!
18820             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18821         }
18822         
18823         if(!dv.length){
18824             return;
18825         }
18826         
18827         var choice = this.choices.createChild({
18828             tag: 'li',
18829             cls: 'roo-select2-search-choice',
18830             cn: [
18831                 {
18832                     tag: 'div',
18833                     html: dv
18834                 },
18835                 {
18836                     tag: 'a',
18837                     href: '#',
18838                     cls: 'roo-select2-search-choice-close fa fa-times',
18839                     tabindex: '-1'
18840                 }
18841             ]
18842             
18843         }, this.searchField);
18844         
18845         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18846         
18847         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18848         
18849         this.item.push(o);
18850         
18851         this.lastData = o;
18852         
18853         this.syncValue();
18854         
18855         this.inputEl().dom.value = '';
18856         
18857         this.validate();
18858     },
18859     
18860     onRemoveItem : function(e, _self, o)
18861     {
18862         e.preventDefault();
18863         
18864         this.lastItem = Roo.apply([], this.item);
18865         
18866         var index = this.item.indexOf(o.data) * 1;
18867         
18868         if( index < 0){
18869             Roo.log('not this item?!');
18870             return;
18871         }
18872         
18873         this.item.splice(index, 1);
18874         o.item.remove();
18875         
18876         this.syncValue();
18877         
18878         this.fireEvent('remove', this, e);
18879         
18880         this.validate();
18881         
18882     },
18883     
18884     syncValue : function()
18885     {
18886         if(!this.item.length){
18887             this.clearValue();
18888             return;
18889         }
18890             
18891         var value = [];
18892         var _this = this;
18893         Roo.each(this.item, function(i){
18894             if(_this.valueField){
18895                 value.push(i[_this.valueField]);
18896                 return;
18897             }
18898
18899             value.push(i);
18900         });
18901
18902         this.value = value.join(',');
18903
18904         if(this.hiddenField){
18905             this.hiddenField.dom.value = this.value;
18906         }
18907         
18908         this.store.fireEvent("datachanged", this.store);
18909         
18910         this.validate();
18911     },
18912     
18913     clearItem : function()
18914     {
18915         if(!this.multiple){
18916             return;
18917         }
18918         
18919         this.item = [];
18920         
18921         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18922            c.remove();
18923         });
18924         
18925         this.syncValue();
18926         
18927         this.validate();
18928         
18929         if(this.tickable && !Roo.isTouch){
18930             this.view.refresh();
18931         }
18932     },
18933     
18934     inputEl: function ()
18935     {
18936         if(Roo.isIOS && this.useNativeIOS){
18937             return this.el.select('select.roo-ios-select', true).first();
18938         }
18939         
18940         if(Roo.isTouch && this.mobileTouchView){
18941             return this.el.select('input.form-control',true).first();
18942         }
18943         
18944         if(this.tickable){
18945             return this.searchField;
18946         }
18947         
18948         return this.el.select('input.form-control',true).first();
18949     },
18950     
18951     onTickableFooterButtonClick : function(e, btn, el)
18952     {
18953         e.preventDefault();
18954         
18955         this.lastItem = Roo.apply([], this.item);
18956         
18957         if(btn && btn.name == 'cancel'){
18958             this.tickItems = Roo.apply([], this.item);
18959             this.collapse();
18960             return;
18961         }
18962         
18963         this.clearItem();
18964         
18965         var _this = this;
18966         
18967         Roo.each(this.tickItems, function(o){
18968             _this.addItem(o);
18969         });
18970         
18971         this.collapse();
18972         
18973     },
18974     
18975     validate : function()
18976     {
18977         if(this.getVisibilityEl().hasClass('hidden')){
18978             return true;
18979         }
18980         
18981         var v = this.getRawValue();
18982         
18983         if(this.multiple){
18984             v = this.getValue();
18985         }
18986         
18987         if(this.disabled || this.allowBlank || v.length){
18988             this.markValid();
18989             return true;
18990         }
18991         
18992         this.markInvalid();
18993         return false;
18994     },
18995     
18996     tickableInputEl : function()
18997     {
18998         if(!this.tickable || !this.editable){
18999             return this.inputEl();
19000         }
19001         
19002         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19003     },
19004     
19005     
19006     getAutoCreateTouchView : function()
19007     {
19008         var id = Roo.id();
19009         
19010         var cfg = {
19011             cls: 'form-group' //input-group
19012         };
19013         
19014         var input =  {
19015             tag: 'input',
19016             id : id,
19017             type : this.inputType,
19018             cls : 'form-control x-combo-noedit',
19019             autocomplete: 'new-password',
19020             placeholder : this.placeholder || '',
19021             readonly : true
19022         };
19023         
19024         if (this.name) {
19025             input.name = this.name;
19026         }
19027         
19028         if (this.size) {
19029             input.cls += ' input-' + this.size;
19030         }
19031         
19032         if (this.disabled) {
19033             input.disabled = true;
19034         }
19035         
19036         var inputblock = {
19037             cls : 'roo-combobox-wrap',
19038             cn : [
19039                 input
19040             ]
19041         };
19042         
19043         if(this.before){
19044             inputblock.cls += ' input-group';
19045             
19046             inputblock.cn.unshift({
19047                 tag :'span',
19048                 cls : 'input-group-addon input-group-prepend input-group-text',
19049                 html : this.before
19050             });
19051         }
19052         
19053         if(this.removable && !this.multiple){
19054             inputblock.cls += ' roo-removable';
19055             
19056             inputblock.cn.push({
19057                 tag: 'button',
19058                 html : 'x',
19059                 cls : 'roo-combo-removable-btn close'
19060             });
19061         }
19062
19063         if(this.hasFeedback && !this.allowBlank){
19064             
19065             inputblock.cls += ' has-feedback';
19066             
19067             inputblock.cn.push({
19068                 tag: 'span',
19069                 cls: 'glyphicon form-control-feedback'
19070             });
19071             
19072         }
19073         
19074         if (this.after) {
19075             
19076             inputblock.cls += (this.before) ? '' : ' input-group';
19077             
19078             inputblock.cn.push({
19079                 tag :'span',
19080                 cls : 'input-group-addon input-group-append input-group-text',
19081                 html : this.after
19082             });
19083         }
19084
19085         
19086         var ibwrap = inputblock;
19087         
19088         if(this.multiple){
19089             ibwrap = {
19090                 tag: 'ul',
19091                 cls: 'roo-select2-choices',
19092                 cn:[
19093                     {
19094                         tag: 'li',
19095                         cls: 'roo-select2-search-field',
19096                         cn: [
19097
19098                             inputblock
19099                         ]
19100                     }
19101                 ]
19102             };
19103         
19104             
19105         }
19106         
19107         var combobox = {
19108             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19109             cn: [
19110                 {
19111                     tag: 'input',
19112                     type : 'hidden',
19113                     cls: 'form-hidden-field'
19114                 },
19115                 ibwrap
19116             ]
19117         };
19118         
19119         if(!this.multiple && this.showToggleBtn){
19120             
19121             var caret = {
19122                 cls: 'caret'
19123             };
19124             
19125             if (this.caret != false) {
19126                 caret = {
19127                      tag: 'i',
19128                      cls: 'fa fa-' + this.caret
19129                 };
19130                 
19131             }
19132             
19133             combobox.cn.push({
19134                 tag :'span',
19135                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19136                 cn : [
19137                     Roo.bootstrap.version == 3 ? caret : '',
19138                     {
19139                         tag: 'span',
19140                         cls: 'combobox-clear',
19141                         cn  : [
19142                             {
19143                                 tag : 'i',
19144                                 cls: 'icon-remove'
19145                             }
19146                         ]
19147                     }
19148                 ]
19149
19150             })
19151         }
19152         
19153         if(this.multiple){
19154             combobox.cls += ' roo-select2-container-multi';
19155         }
19156         
19157         var required =  this.allowBlank ?  {
19158                     tag : 'i',
19159                     style: 'display: none'
19160                 } : {
19161                    tag : 'i',
19162                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19163                    tooltip : 'This field is required'
19164                 };
19165         
19166         var align = this.labelAlign || this.parentLabelAlign();
19167         
19168         if (align ==='left' && this.fieldLabel.length) {
19169
19170             cfg.cn = [
19171                 required,
19172                 {
19173                     tag: 'label',
19174                     cls : 'control-label col-form-label',
19175                     html : this.fieldLabel
19176
19177                 },
19178                 {
19179                     cls : 'roo-combobox-wrap ', 
19180                     cn: [
19181                         combobox
19182                     ]
19183                 }
19184             ];
19185             
19186             var labelCfg = cfg.cn[1];
19187             var contentCfg = cfg.cn[2];
19188             
19189
19190             if(this.indicatorpos == 'right'){
19191                 cfg.cn = [
19192                     {
19193                         tag: 'label',
19194                         'for' :  id,
19195                         cls : 'control-label col-form-label',
19196                         cn : [
19197                             {
19198                                 tag : 'span',
19199                                 html : this.fieldLabel
19200                             },
19201                             required
19202                         ]
19203                     },
19204                     {
19205                         cls : "roo-combobox-wrap ",
19206                         cn: [
19207                             combobox
19208                         ]
19209                     }
19210
19211                 ];
19212                 
19213                 labelCfg = cfg.cn[0];
19214                 contentCfg = cfg.cn[1];
19215             }
19216             
19217            
19218             
19219             if(this.labelWidth > 12){
19220                 labelCfg.style = "width: " + this.labelWidth + 'px';
19221             }
19222            
19223             if(this.labelWidth < 13 && this.labelmd == 0){
19224                 this.labelmd = this.labelWidth;
19225             }
19226             
19227             if(this.labellg > 0){
19228                 labelCfg.cls += ' col-lg-' + this.labellg;
19229                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19230             }
19231             
19232             if(this.labelmd > 0){
19233                 labelCfg.cls += ' col-md-' + this.labelmd;
19234                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19235             }
19236             
19237             if(this.labelsm > 0){
19238                 labelCfg.cls += ' col-sm-' + this.labelsm;
19239                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19240             }
19241             
19242             if(this.labelxs > 0){
19243                 labelCfg.cls += ' col-xs-' + this.labelxs;
19244                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19245             }
19246                 
19247                 
19248         } else if ( this.fieldLabel.length) {
19249             cfg.cn = [
19250                required,
19251                 {
19252                     tag: 'label',
19253                     cls : 'control-label',
19254                     html : this.fieldLabel
19255
19256                 },
19257                 {
19258                     cls : '', 
19259                     cn: [
19260                         combobox
19261                     ]
19262                 }
19263             ];
19264             
19265             if(this.indicatorpos == 'right'){
19266                 cfg.cn = [
19267                     {
19268                         tag: 'label',
19269                         cls : 'control-label',
19270                         html : this.fieldLabel,
19271                         cn : [
19272                             required
19273                         ]
19274                     },
19275                     {
19276                         cls : '', 
19277                         cn: [
19278                             combobox
19279                         ]
19280                     }
19281                 ];
19282             }
19283         } else {
19284             cfg.cn = combobox;    
19285         }
19286         
19287         
19288         var settings = this;
19289         
19290         ['xs','sm','md','lg'].map(function(size){
19291             if (settings[size]) {
19292                 cfg.cls += ' col-' + size + '-' + settings[size];
19293             }
19294         });
19295         
19296         return cfg;
19297     },
19298     
19299     initTouchView : function()
19300     {
19301         this.renderTouchView();
19302         
19303         this.touchViewEl.on('scroll', function(){
19304             this.el.dom.scrollTop = 0;
19305         }, this);
19306         
19307         this.originalValue = this.getValue();
19308         
19309         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19310         
19311         this.inputEl().on("click", this.showTouchView, this);
19312         if (this.triggerEl) {
19313             this.triggerEl.on("click", this.showTouchView, this);
19314         }
19315         
19316         
19317         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19318         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19319         
19320         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19321         
19322         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19323         this.store.on('load', this.onTouchViewLoad, this);
19324         this.store.on('loadexception', this.onTouchViewLoadException, this);
19325         
19326         if(this.hiddenName){
19327             
19328             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19329             
19330             this.hiddenField.dom.value =
19331                 this.hiddenValue !== undefined ? this.hiddenValue :
19332                 this.value !== undefined ? this.value : '';
19333         
19334             this.el.dom.removeAttribute('name');
19335             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19336         }
19337         
19338         if(this.multiple){
19339             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19340             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19341         }
19342         
19343         if(this.removable && !this.multiple){
19344             var close = this.closeTriggerEl();
19345             if(close){
19346                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19347                 close.on('click', this.removeBtnClick, this, close);
19348             }
19349         }
19350         /*
19351          * fix the bug in Safari iOS8
19352          */
19353         this.inputEl().on("focus", function(e){
19354             document.activeElement.blur();
19355         }, this);
19356         
19357         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19358         
19359         return;
19360         
19361         
19362     },
19363     
19364     renderTouchView : function()
19365     {
19366         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19367         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19368         
19369         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19370         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19371         
19372         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19373         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19374         this.touchViewBodyEl.setStyle('overflow', 'auto');
19375         
19376         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19377         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19378         
19379         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19380         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19381         
19382     },
19383     
19384     showTouchView : function()
19385     {
19386         if(this.disabled){
19387             return;
19388         }
19389         
19390         this.touchViewHeaderEl.hide();
19391
19392         if(this.modalTitle.length){
19393             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19394             this.touchViewHeaderEl.show();
19395         }
19396
19397         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19398         this.touchViewEl.show();
19399
19400         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19401         
19402         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19403         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19404
19405         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19406
19407         if(this.modalTitle.length){
19408             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19409         }
19410         
19411         this.touchViewBodyEl.setHeight(bodyHeight);
19412
19413         if(this.animate){
19414             var _this = this;
19415             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19416         }else{
19417             this.touchViewEl.addClass(['in','show']);
19418         }
19419         
19420         if(this._touchViewMask){
19421             Roo.get(document.body).addClass("x-body-masked");
19422             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19423             this._touchViewMask.setStyle('z-index', 10000);
19424             this._touchViewMask.addClass('show');
19425         }
19426         
19427         this.doTouchViewQuery();
19428         
19429     },
19430     
19431     hideTouchView : function()
19432     {
19433         this.touchViewEl.removeClass(['in','show']);
19434
19435         if(this.animate){
19436             var _this = this;
19437             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19438         }else{
19439             this.touchViewEl.setStyle('display', 'none');
19440         }
19441         
19442         if(this._touchViewMask){
19443             this._touchViewMask.removeClass('show');
19444             Roo.get(document.body).removeClass("x-body-masked");
19445         }
19446     },
19447     
19448     setTouchViewValue : function()
19449     {
19450         if(this.multiple){
19451             this.clearItem();
19452         
19453             var _this = this;
19454
19455             Roo.each(this.tickItems, function(o){
19456                 this.addItem(o);
19457             }, this);
19458         }
19459         
19460         this.hideTouchView();
19461     },
19462     
19463     doTouchViewQuery : function()
19464     {
19465         var qe = {
19466             query: '',
19467             forceAll: true,
19468             combo: this,
19469             cancel:false
19470         };
19471         
19472         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19473             return false;
19474         }
19475         
19476         if(!this.alwaysQuery || this.mode == 'local'){
19477             this.onTouchViewLoad();
19478             return;
19479         }
19480         
19481         this.store.load();
19482     },
19483     
19484     onTouchViewBeforeLoad : function(combo,opts)
19485     {
19486         return;
19487     },
19488
19489     // private
19490     onTouchViewLoad : function()
19491     {
19492         if(this.store.getCount() < 1){
19493             this.onTouchViewEmptyResults();
19494             return;
19495         }
19496         
19497         this.clearTouchView();
19498         
19499         var rawValue = this.getRawValue();
19500         
19501         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19502         
19503         this.tickItems = [];
19504         
19505         this.store.data.each(function(d, rowIndex){
19506             var row = this.touchViewListGroup.createChild(template);
19507             
19508             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19509                 row.addClass(d.data.cls);
19510             }
19511             
19512             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19513                 var cfg = {
19514                     data : d.data,
19515                     html : d.data[this.displayField]
19516                 };
19517                 
19518                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19519                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19520                 }
19521             }
19522             row.removeClass('selected');
19523             if(!this.multiple && this.valueField &&
19524                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19525             {
19526                 // radio buttons..
19527                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19528                 row.addClass('selected');
19529             }
19530             
19531             if(this.multiple && this.valueField &&
19532                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19533             {
19534                 
19535                 // checkboxes...
19536                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19537                 this.tickItems.push(d.data);
19538             }
19539             
19540             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19541             
19542         }, this);
19543         
19544         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19545         
19546         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19547
19548         if(this.modalTitle.length){
19549             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19550         }
19551
19552         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19553         
19554         if(this.mobile_restrict_height && listHeight < bodyHeight){
19555             this.touchViewBodyEl.setHeight(listHeight);
19556         }
19557         
19558         var _this = this;
19559         
19560         if(firstChecked && listHeight > bodyHeight){
19561             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19562         }
19563         
19564     },
19565     
19566     onTouchViewLoadException : function()
19567     {
19568         this.hideTouchView();
19569     },
19570     
19571     onTouchViewEmptyResults : function()
19572     {
19573         this.clearTouchView();
19574         
19575         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19576         
19577         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19578         
19579     },
19580     
19581     clearTouchView : function()
19582     {
19583         this.touchViewListGroup.dom.innerHTML = '';
19584     },
19585     
19586     onTouchViewClick : function(e, el, o)
19587     {
19588         e.preventDefault();
19589         
19590         var row = o.row;
19591         var rowIndex = o.rowIndex;
19592         
19593         var r = this.store.getAt(rowIndex);
19594         
19595         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19596             
19597             if(!this.multiple){
19598                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19599                     c.dom.removeAttribute('checked');
19600                 }, this);
19601
19602                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19603
19604                 this.setFromData(r.data);
19605
19606                 var close = this.closeTriggerEl();
19607
19608                 if(close){
19609                     close.show();
19610                 }
19611
19612                 this.hideTouchView();
19613
19614                 this.fireEvent('select', this, r, rowIndex);
19615
19616                 return;
19617             }
19618
19619             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19620                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19621                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19622                 return;
19623             }
19624
19625             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19626             this.addItem(r.data);
19627             this.tickItems.push(r.data);
19628         }
19629     },
19630     
19631     getAutoCreateNativeIOS : function()
19632     {
19633         var cfg = {
19634             cls: 'form-group' //input-group,
19635         };
19636         
19637         var combobox =  {
19638             tag: 'select',
19639             cls : 'roo-ios-select'
19640         };
19641         
19642         if (this.name) {
19643             combobox.name = this.name;
19644         }
19645         
19646         if (this.disabled) {
19647             combobox.disabled = true;
19648         }
19649         
19650         var settings = this;
19651         
19652         ['xs','sm','md','lg'].map(function(size){
19653             if (settings[size]) {
19654                 cfg.cls += ' col-' + size + '-' + settings[size];
19655             }
19656         });
19657         
19658         cfg.cn = combobox;
19659         
19660         return cfg;
19661         
19662     },
19663     
19664     initIOSView : function()
19665     {
19666         this.store.on('load', this.onIOSViewLoad, this);
19667         
19668         return;
19669     },
19670     
19671     onIOSViewLoad : function()
19672     {
19673         if(this.store.getCount() < 1){
19674             return;
19675         }
19676         
19677         this.clearIOSView();
19678         
19679         if(this.allowBlank) {
19680             
19681             var default_text = '-- SELECT --';
19682             
19683             if(this.placeholder.length){
19684                 default_text = this.placeholder;
19685             }
19686             
19687             if(this.emptyTitle.length){
19688                 default_text += ' - ' + this.emptyTitle + ' -';
19689             }
19690             
19691             var opt = this.inputEl().createChild({
19692                 tag: 'option',
19693                 value : 0,
19694                 html : default_text
19695             });
19696             
19697             var o = {};
19698             o[this.valueField] = 0;
19699             o[this.displayField] = default_text;
19700             
19701             this.ios_options.push({
19702                 data : o,
19703                 el : opt
19704             });
19705             
19706         }
19707         
19708         this.store.data.each(function(d, rowIndex){
19709             
19710             var html = '';
19711             
19712             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19713                 html = d.data[this.displayField];
19714             }
19715             
19716             var value = '';
19717             
19718             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19719                 value = d.data[this.valueField];
19720             }
19721             
19722             var option = {
19723                 tag: 'option',
19724                 value : value,
19725                 html : html
19726             };
19727             
19728             if(this.value == d.data[this.valueField]){
19729                 option['selected'] = true;
19730             }
19731             
19732             var opt = this.inputEl().createChild(option);
19733             
19734             this.ios_options.push({
19735                 data : d.data,
19736                 el : opt
19737             });
19738             
19739         }, this);
19740         
19741         this.inputEl().on('change', function(){
19742            this.fireEvent('select', this);
19743         }, this);
19744         
19745     },
19746     
19747     clearIOSView: function()
19748     {
19749         this.inputEl().dom.innerHTML = '';
19750         
19751         this.ios_options = [];
19752     },
19753     
19754     setIOSValue: function(v)
19755     {
19756         this.value = v;
19757         
19758         if(!this.ios_options){
19759             return;
19760         }
19761         
19762         Roo.each(this.ios_options, function(opts){
19763            
19764            opts.el.dom.removeAttribute('selected');
19765            
19766            if(opts.data[this.valueField] != v){
19767                return;
19768            }
19769            
19770            opts.el.dom.setAttribute('selected', true);
19771            
19772         }, this);
19773     }
19774
19775     /** 
19776     * @cfg {Boolean} grow 
19777     * @hide 
19778     */
19779     /** 
19780     * @cfg {Number} growMin 
19781     * @hide 
19782     */
19783     /** 
19784     * @cfg {Number} growMax 
19785     * @hide 
19786     */
19787     /**
19788      * @hide
19789      * @method autoSize
19790      */
19791 });
19792
19793 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19794     
19795     header : {
19796         tag: 'div',
19797         cls: 'modal-header',
19798         cn: [
19799             {
19800                 tag: 'h4',
19801                 cls: 'modal-title'
19802             }
19803         ]
19804     },
19805     
19806     body : {
19807         tag: 'div',
19808         cls: 'modal-body',
19809         cn: [
19810             {
19811                 tag: 'ul',
19812                 cls: 'list-group'
19813             }
19814         ]
19815     },
19816     
19817     listItemRadio : {
19818         tag: 'li',
19819         cls: 'list-group-item',
19820         cn: [
19821             {
19822                 tag: 'span',
19823                 cls: 'roo-combobox-list-group-item-value'
19824             },
19825             {
19826                 tag: 'div',
19827                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19828                 cn: [
19829                     {
19830                         tag: 'input',
19831                         type: 'radio'
19832                     },
19833                     {
19834                         tag: 'label'
19835                     }
19836                 ]
19837             }
19838         ]
19839     },
19840     
19841     listItemCheckbox : {
19842         tag: 'li',
19843         cls: 'list-group-item',
19844         cn: [
19845             {
19846                 tag: 'span',
19847                 cls: 'roo-combobox-list-group-item-value'
19848             },
19849             {
19850                 tag: 'div',
19851                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19852                 cn: [
19853                     {
19854                         tag: 'input',
19855                         type: 'checkbox'
19856                     },
19857                     {
19858                         tag: 'label'
19859                     }
19860                 ]
19861             }
19862         ]
19863     },
19864     
19865     emptyResult : {
19866         tag: 'div',
19867         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19868     },
19869     
19870     footer : {
19871         tag: 'div',
19872         cls: 'modal-footer',
19873         cn: [
19874             {
19875                 tag: 'div',
19876                 cls: 'row',
19877                 cn: [
19878                     {
19879                         tag: 'div',
19880                         cls: 'col-xs-6 text-left',
19881                         cn: {
19882                             tag: 'button',
19883                             cls: 'btn btn-danger roo-touch-view-cancel',
19884                             html: 'Cancel'
19885                         }
19886                     },
19887                     {
19888                         tag: 'div',
19889                         cls: 'col-xs-6 text-right',
19890                         cn: {
19891                             tag: 'button',
19892                             cls: 'btn btn-success roo-touch-view-ok',
19893                             html: 'OK'
19894                         }
19895                     }
19896                 ]
19897             }
19898         ]
19899         
19900     }
19901 });
19902
19903 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19904     
19905     touchViewTemplate : {
19906         tag: 'div',
19907         cls: 'modal fade roo-combobox-touch-view',
19908         cn: [
19909             {
19910                 tag: 'div',
19911                 cls: 'modal-dialog',
19912                 style : 'position:fixed', // we have to fix position....
19913                 cn: [
19914                     {
19915                         tag: 'div',
19916                         cls: 'modal-content',
19917                         cn: [
19918                             Roo.bootstrap.form.ComboBox.header,
19919                             Roo.bootstrap.form.ComboBox.body,
19920                             Roo.bootstrap.form.ComboBox.footer
19921                         ]
19922                     }
19923                 ]
19924             }
19925         ]
19926     }
19927 });/*
19928  * Based on:
19929  * Ext JS Library 1.1.1
19930  * Copyright(c) 2006-2007, Ext JS, LLC.
19931  *
19932  * Originally Released Under LGPL - original licence link has changed is not relivant.
19933  *
19934  * Fork - LGPL
19935  * <script type="text/javascript">
19936  */
19937
19938 /**
19939  * @class Roo.View
19940  * @extends Roo.util.Observable
19941  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19942  * This class also supports single and multi selection modes. <br>
19943  * Create a data model bound view:
19944  <pre><code>
19945  var store = new Roo.data.Store(...);
19946
19947  var view = new Roo.View({
19948     el : "my-element",
19949     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19950  
19951     singleSelect: true,
19952     selectedClass: "ydataview-selected",
19953     store: store
19954  });
19955
19956  // listen for node click?
19957  view.on("click", function(vw, index, node, e){
19958  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19959  });
19960
19961  // load XML data
19962  dataModel.load("foobar.xml");
19963  </code></pre>
19964  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19965  * <br><br>
19966  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19967  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19968  * 
19969  * Note: old style constructor is still suported (container, template, config)
19970  * 
19971  * @constructor
19972  * Create a new View
19973  * @param {Object} config The config object
19974  * 
19975  */
19976 Roo.View = function(config, depreciated_tpl, depreciated_config){
19977     
19978     this.parent = false;
19979     
19980     if (typeof(depreciated_tpl) == 'undefined') {
19981         // new way.. - universal constructor.
19982         Roo.apply(this, config);
19983         this.el  = Roo.get(this.el);
19984     } else {
19985         // old format..
19986         this.el  = Roo.get(config);
19987         this.tpl = depreciated_tpl;
19988         Roo.apply(this, depreciated_config);
19989     }
19990     this.wrapEl  = this.el.wrap().wrap();
19991     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19992     
19993     
19994     if(typeof(this.tpl) == "string"){
19995         this.tpl = new Roo.Template(this.tpl);
19996     } else {
19997         // support xtype ctors..
19998         this.tpl = new Roo.factory(this.tpl, Roo);
19999     }
20000     
20001     
20002     this.tpl.compile();
20003     
20004     /** @private */
20005     this.addEvents({
20006         /**
20007          * @event beforeclick
20008          * Fires before a click is processed. Returns false to cancel the default action.
20009          * @param {Roo.View} this
20010          * @param {Number} index The index of the target node
20011          * @param {HTMLElement} node The target node
20012          * @param {Roo.EventObject} e The raw event object
20013          */
20014             "beforeclick" : true,
20015         /**
20016          * @event click
20017          * Fires when a template node is clicked.
20018          * @param {Roo.View} this
20019          * @param {Number} index The index of the target node
20020          * @param {HTMLElement} node The target node
20021          * @param {Roo.EventObject} e The raw event object
20022          */
20023             "click" : true,
20024         /**
20025          * @event dblclick
20026          * Fires when a template node is double clicked.
20027          * @param {Roo.View} this
20028          * @param {Number} index The index of the target node
20029          * @param {HTMLElement} node The target node
20030          * @param {Roo.EventObject} e The raw event object
20031          */
20032             "dblclick" : true,
20033         /**
20034          * @event contextmenu
20035          * Fires when a template node is right clicked.
20036          * @param {Roo.View} this
20037          * @param {Number} index The index of the target node
20038          * @param {HTMLElement} node The target node
20039          * @param {Roo.EventObject} e The raw event object
20040          */
20041             "contextmenu" : true,
20042         /**
20043          * @event selectionchange
20044          * Fires when the selected nodes change.
20045          * @param {Roo.View} this
20046          * @param {Array} selections Array of the selected nodes
20047          */
20048             "selectionchange" : true,
20049     
20050         /**
20051          * @event beforeselect
20052          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20053          * @param {Roo.View} this
20054          * @param {HTMLElement} node The node to be selected
20055          * @param {Array} selections Array of currently selected nodes
20056          */
20057             "beforeselect" : true,
20058         /**
20059          * @event preparedata
20060          * Fires on every row to render, to allow you to change the data.
20061          * @param {Roo.View} this
20062          * @param {Object} data to be rendered (change this)
20063          */
20064           "preparedata" : true
20065           
20066           
20067         });
20068
20069
20070
20071     this.el.on({
20072         "click": this.onClick,
20073         "dblclick": this.onDblClick,
20074         "contextmenu": this.onContextMenu,
20075         scope:this
20076     });
20077
20078     this.selections = [];
20079     this.nodes = [];
20080     this.cmp = new Roo.CompositeElementLite([]);
20081     if(this.store){
20082         this.store = Roo.factory(this.store, Roo.data);
20083         this.setStore(this.store, true);
20084     }
20085     
20086     if ( this.footer && this.footer.xtype) {
20087            
20088          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20089         
20090         this.footer.dataSource = this.store;
20091         this.footer.container = fctr;
20092         this.footer = Roo.factory(this.footer, Roo);
20093         fctr.insertFirst(this.el);
20094         
20095         // this is a bit insane - as the paging toolbar seems to detach the el..
20096 //        dom.parentNode.parentNode.parentNode
20097          // they get detached?
20098     }
20099     
20100     
20101     Roo.View.superclass.constructor.call(this);
20102     
20103     
20104 };
20105
20106 Roo.extend(Roo.View, Roo.util.Observable, {
20107     
20108      /**
20109      * @cfg {Roo.data.Store} store Data store to load data from.
20110      */
20111     store : false,
20112     
20113     /**
20114      * @cfg {String|Roo.Element} el The container element.
20115      */
20116     el : '',
20117     
20118     /**
20119      * @cfg {String|Roo.Template} tpl The template used by this View 
20120      */
20121     tpl : false,
20122     /**
20123      * @cfg {String} dataName the named area of the template to use as the data area
20124      *                          Works with domtemplates roo-name="name"
20125      */
20126     dataName: false,
20127     /**
20128      * @cfg {String} selectedClass The css class to add to selected nodes
20129      */
20130     selectedClass : "x-view-selected",
20131      /**
20132      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20133      */
20134     emptyText : "",
20135     
20136     /**
20137      * @cfg {String} text to display on mask (default Loading)
20138      */
20139     mask : false,
20140     /**
20141      * @cfg {Boolean} multiSelect Allow multiple selection
20142      */
20143     multiSelect : false,
20144     /**
20145      * @cfg {Boolean} singleSelect Allow single selection
20146      */
20147     singleSelect:  false,
20148     
20149     /**
20150      * @cfg {Boolean} toggleSelect - selecting 
20151      */
20152     toggleSelect : false,
20153     
20154     /**
20155      * @cfg {Boolean} tickable - selecting 
20156      */
20157     tickable : false,
20158     
20159     /**
20160      * Returns the element this view is bound to.
20161      * @return {Roo.Element}
20162      */
20163     getEl : function(){
20164         return this.wrapEl;
20165     },
20166     
20167     
20168
20169     /**
20170      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20171      */
20172     refresh : function(){
20173         //Roo.log('refresh');
20174         var t = this.tpl;
20175         
20176         // if we are using something like 'domtemplate', then
20177         // the what gets used is:
20178         // t.applySubtemplate(NAME, data, wrapping data..)
20179         // the outer template then get' applied with
20180         //     the store 'extra data'
20181         // and the body get's added to the
20182         //      roo-name="data" node?
20183         //      <span class='roo-tpl-{name}'></span> ?????
20184         
20185         
20186         
20187         this.clearSelections();
20188         this.el.update("");
20189         var html = [];
20190         var records = this.store.getRange();
20191         if(records.length < 1) {
20192             
20193             // is this valid??  = should it render a template??
20194             
20195             this.el.update(this.emptyText);
20196             return;
20197         }
20198         var el = this.el;
20199         if (this.dataName) {
20200             this.el.update(t.apply(this.store.meta)); //????
20201             el = this.el.child('.roo-tpl-' + this.dataName);
20202         }
20203         
20204         for(var i = 0, len = records.length; i < len; i++){
20205             var data = this.prepareData(records[i].data, i, records[i]);
20206             this.fireEvent("preparedata", this, data, i, records[i]);
20207             
20208             var d = Roo.apply({}, data);
20209             
20210             if(this.tickable){
20211                 Roo.apply(d, {'roo-id' : Roo.id()});
20212                 
20213                 var _this = this;
20214             
20215                 Roo.each(this.parent.item, function(item){
20216                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20217                         return;
20218                     }
20219                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20220                 });
20221             }
20222             
20223             html[html.length] = Roo.util.Format.trim(
20224                 this.dataName ?
20225                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20226                     t.apply(d)
20227             );
20228         }
20229         
20230         
20231         
20232         el.update(html.join(""));
20233         this.nodes = el.dom.childNodes;
20234         this.updateIndexes(0);
20235     },
20236     
20237
20238     /**
20239      * Function to override to reformat the data that is sent to
20240      * the template for each node.
20241      * DEPRICATED - use the preparedata event handler.
20242      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20243      * a JSON object for an UpdateManager bound view).
20244      */
20245     prepareData : function(data, index, record)
20246     {
20247         this.fireEvent("preparedata", this, data, index, record);
20248         return data;
20249     },
20250
20251     onUpdate : function(ds, record){
20252         // Roo.log('on update');   
20253         this.clearSelections();
20254         var index = this.store.indexOf(record);
20255         var n = this.nodes[index];
20256         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20257         n.parentNode.removeChild(n);
20258         this.updateIndexes(index, index);
20259     },
20260
20261     
20262     
20263 // --------- FIXME     
20264     onAdd : function(ds, records, index)
20265     {
20266         //Roo.log(['on Add', ds, records, index] );        
20267         this.clearSelections();
20268         if(this.nodes.length == 0){
20269             this.refresh();
20270             return;
20271         }
20272         var n = this.nodes[index];
20273         for(var i = 0, len = records.length; i < len; i++){
20274             var d = this.prepareData(records[i].data, i, records[i]);
20275             if(n){
20276                 this.tpl.insertBefore(n, d);
20277             }else{
20278                 
20279                 this.tpl.append(this.el, d);
20280             }
20281         }
20282         this.updateIndexes(index);
20283     },
20284
20285     onRemove : function(ds, record, index){
20286        // Roo.log('onRemove');
20287         this.clearSelections();
20288         var el = this.dataName  ?
20289             this.el.child('.roo-tpl-' + this.dataName) :
20290             this.el; 
20291         
20292         el.dom.removeChild(this.nodes[index]);
20293         this.updateIndexes(index);
20294     },
20295
20296     /**
20297      * Refresh an individual node.
20298      * @param {Number} index
20299      */
20300     refreshNode : function(index){
20301         this.onUpdate(this.store, this.store.getAt(index));
20302     },
20303
20304     updateIndexes : function(startIndex, endIndex){
20305         var ns = this.nodes;
20306         startIndex = startIndex || 0;
20307         endIndex = endIndex || ns.length - 1;
20308         for(var i = startIndex; i <= endIndex; i++){
20309             ns[i].nodeIndex = i;
20310         }
20311     },
20312
20313     /**
20314      * Changes the data store this view uses and refresh the view.
20315      * @param {Store} store
20316      */
20317     setStore : function(store, initial){
20318         if(!initial && this.store){
20319             this.store.un("datachanged", this.refresh);
20320             this.store.un("add", this.onAdd);
20321             this.store.un("remove", this.onRemove);
20322             this.store.un("update", this.onUpdate);
20323             this.store.un("clear", this.refresh);
20324             this.store.un("beforeload", this.onBeforeLoad);
20325             this.store.un("load", this.onLoad);
20326             this.store.un("loadexception", this.onLoad);
20327         }
20328         if(store){
20329           
20330             store.on("datachanged", this.refresh, this);
20331             store.on("add", this.onAdd, this);
20332             store.on("remove", this.onRemove, this);
20333             store.on("update", this.onUpdate, this);
20334             store.on("clear", this.refresh, this);
20335             store.on("beforeload", this.onBeforeLoad, this);
20336             store.on("load", this.onLoad, this);
20337             store.on("loadexception", this.onLoad, this);
20338         }
20339         
20340         if(store){
20341             this.refresh();
20342         }
20343     },
20344     /**
20345      * onbeforeLoad - masks the loading area.
20346      *
20347      */
20348     onBeforeLoad : function(store,opts)
20349     {
20350          //Roo.log('onBeforeLoad');   
20351         if (!opts.add) {
20352             this.el.update("");
20353         }
20354         this.el.mask(this.mask ? this.mask : "Loading" ); 
20355     },
20356     onLoad : function ()
20357     {
20358         this.el.unmask();
20359     },
20360     
20361
20362     /**
20363      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20364      * @param {HTMLElement} node
20365      * @return {HTMLElement} The template node
20366      */
20367     findItemFromChild : function(node){
20368         var el = this.dataName  ?
20369             this.el.child('.roo-tpl-' + this.dataName,true) :
20370             this.el.dom; 
20371         
20372         if(!node || node.parentNode == el){
20373                     return node;
20374             }
20375             var p = node.parentNode;
20376             while(p && p != el){
20377             if(p.parentNode == el){
20378                 return p;
20379             }
20380             p = p.parentNode;
20381         }
20382             return null;
20383     },
20384
20385     /** @ignore */
20386     onClick : function(e){
20387         var item = this.findItemFromChild(e.getTarget());
20388         if(item){
20389             var index = this.indexOf(item);
20390             if(this.onItemClick(item, index, e) !== false){
20391                 this.fireEvent("click", this, index, item, e);
20392             }
20393         }else{
20394             this.clearSelections();
20395         }
20396     },
20397
20398     /** @ignore */
20399     onContextMenu : function(e){
20400         var item = this.findItemFromChild(e.getTarget());
20401         if(item){
20402             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20403         }
20404     },
20405
20406     /** @ignore */
20407     onDblClick : function(e){
20408         var item = this.findItemFromChild(e.getTarget());
20409         if(item){
20410             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20411         }
20412     },
20413
20414     onItemClick : function(item, index, e)
20415     {
20416         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20417             return false;
20418         }
20419         if (this.toggleSelect) {
20420             var m = this.isSelected(item) ? 'unselect' : 'select';
20421             //Roo.log(m);
20422             var _t = this;
20423             _t[m](item, true, false);
20424             return true;
20425         }
20426         if(this.multiSelect || this.singleSelect){
20427             if(this.multiSelect && e.shiftKey && this.lastSelection){
20428                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20429             }else{
20430                 this.select(item, this.multiSelect && e.ctrlKey);
20431                 this.lastSelection = item;
20432             }
20433             
20434             if(!this.tickable){
20435                 e.preventDefault();
20436             }
20437             
20438         }
20439         return true;
20440     },
20441
20442     /**
20443      * Get the number of selected nodes.
20444      * @return {Number}
20445      */
20446     getSelectionCount : function(){
20447         return this.selections.length;
20448     },
20449
20450     /**
20451      * Get the currently selected nodes.
20452      * @return {Array} An array of HTMLElements
20453      */
20454     getSelectedNodes : function(){
20455         return this.selections;
20456     },
20457
20458     /**
20459      * Get the indexes of the selected nodes.
20460      * @return {Array}
20461      */
20462     getSelectedIndexes : function(){
20463         var indexes = [], s = this.selections;
20464         for(var i = 0, len = s.length; i < len; i++){
20465             indexes.push(s[i].nodeIndex);
20466         }
20467         return indexes;
20468     },
20469
20470     /**
20471      * Clear all selections
20472      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20473      */
20474     clearSelections : function(suppressEvent){
20475         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20476             this.cmp.elements = this.selections;
20477             this.cmp.removeClass(this.selectedClass);
20478             this.selections = [];
20479             if(!suppressEvent){
20480                 this.fireEvent("selectionchange", this, this.selections);
20481             }
20482         }
20483     },
20484
20485     /**
20486      * Returns true if the passed node is selected
20487      * @param {HTMLElement/Number} node The node or node index
20488      * @return {Boolean}
20489      */
20490     isSelected : function(node){
20491         var s = this.selections;
20492         if(s.length < 1){
20493             return false;
20494         }
20495         node = this.getNode(node);
20496         return s.indexOf(node) !== -1;
20497     },
20498
20499     /**
20500      * Selects nodes.
20501      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20502      * @param {Boolean} keepExisting (optional) true to keep existing selections
20503      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20504      */
20505     select : function(nodeInfo, keepExisting, suppressEvent){
20506         if(nodeInfo instanceof Array){
20507             if(!keepExisting){
20508                 this.clearSelections(true);
20509             }
20510             for(var i = 0, len = nodeInfo.length; i < len; i++){
20511                 this.select(nodeInfo[i], true, true);
20512             }
20513             return;
20514         } 
20515         var node = this.getNode(nodeInfo);
20516         if(!node || this.isSelected(node)){
20517             return; // already selected.
20518         }
20519         if(!keepExisting){
20520             this.clearSelections(true);
20521         }
20522         
20523         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20524             Roo.fly(node).addClass(this.selectedClass);
20525             this.selections.push(node);
20526             if(!suppressEvent){
20527                 this.fireEvent("selectionchange", this, this.selections);
20528             }
20529         }
20530         
20531         
20532     },
20533       /**
20534      * Unselects nodes.
20535      * @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
20536      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20537      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20538      */
20539     unselect : function(nodeInfo, keepExisting, suppressEvent)
20540     {
20541         if(nodeInfo instanceof Array){
20542             Roo.each(this.selections, function(s) {
20543                 this.unselect(s, nodeInfo);
20544             }, this);
20545             return;
20546         }
20547         var node = this.getNode(nodeInfo);
20548         if(!node || !this.isSelected(node)){
20549             //Roo.log("not selected");
20550             return; // not selected.
20551         }
20552         // fireevent???
20553         var ns = [];
20554         Roo.each(this.selections, function(s) {
20555             if (s == node ) {
20556                 Roo.fly(node).removeClass(this.selectedClass);
20557
20558                 return;
20559             }
20560             ns.push(s);
20561         },this);
20562         
20563         this.selections= ns;
20564         this.fireEvent("selectionchange", this, this.selections);
20565     },
20566
20567     /**
20568      * Gets a template node.
20569      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20570      * @return {HTMLElement} The node or null if it wasn't found
20571      */
20572     getNode : function(nodeInfo){
20573         if(typeof nodeInfo == "string"){
20574             return document.getElementById(nodeInfo);
20575         }else if(typeof nodeInfo == "number"){
20576             return this.nodes[nodeInfo];
20577         }
20578         return nodeInfo;
20579     },
20580
20581     /**
20582      * Gets a range template nodes.
20583      * @param {Number} startIndex
20584      * @param {Number} endIndex
20585      * @return {Array} An array of nodes
20586      */
20587     getNodes : function(start, end){
20588         var ns = this.nodes;
20589         start = start || 0;
20590         end = typeof end == "undefined" ? ns.length - 1 : end;
20591         var nodes = [];
20592         if(start <= end){
20593             for(var i = start; i <= end; i++){
20594                 nodes.push(ns[i]);
20595             }
20596         } else{
20597             for(var i = start; i >= end; i--){
20598                 nodes.push(ns[i]);
20599             }
20600         }
20601         return nodes;
20602     },
20603
20604     /**
20605      * Finds the index of the passed node
20606      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20607      * @return {Number} The index of the node or -1
20608      */
20609     indexOf : function(node){
20610         node = this.getNode(node);
20611         if(typeof node.nodeIndex == "number"){
20612             return node.nodeIndex;
20613         }
20614         var ns = this.nodes;
20615         for(var i = 0, len = ns.length; i < len; i++){
20616             if(ns[i] == node){
20617                 return i;
20618             }
20619         }
20620         return -1;
20621     }
20622 });
20623 /*
20624  * - LGPL
20625  *
20626  * based on jquery fullcalendar
20627  * 
20628  */
20629
20630 Roo.bootstrap = Roo.bootstrap || {};
20631 /**
20632  * @class Roo.bootstrap.Calendar
20633  * @extends Roo.bootstrap.Component
20634  * Bootstrap Calendar class
20635  * @cfg {Boolean} loadMask (true|false) default false
20636  * @cfg {Object} header generate the user specific header of the calendar, default false
20637
20638  * @constructor
20639  * Create a new Container
20640  * @param {Object} config The config object
20641  */
20642
20643
20644
20645 Roo.bootstrap.Calendar = function(config){
20646     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20647      this.addEvents({
20648         /**
20649              * @event select
20650              * Fires when a date is selected
20651              * @param {DatePicker} this
20652              * @param {Date} date The selected date
20653              */
20654         'select': true,
20655         /**
20656              * @event monthchange
20657              * Fires when the displayed month changes 
20658              * @param {DatePicker} this
20659              * @param {Date} date The selected month
20660              */
20661         'monthchange': true,
20662         /**
20663              * @event evententer
20664              * Fires when mouse over an event
20665              * @param {Calendar} this
20666              * @param {event} Event
20667              */
20668         'evententer': true,
20669         /**
20670              * @event eventleave
20671              * Fires when the mouse leaves an
20672              * @param {Calendar} this
20673              * @param {event}
20674              */
20675         'eventleave': true,
20676         /**
20677              * @event eventclick
20678              * Fires when the mouse click an
20679              * @param {Calendar} this
20680              * @param {event}
20681              */
20682         'eventclick': true
20683         
20684     });
20685
20686 };
20687
20688 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20689     
20690           /**
20691      * @cfg {Roo.data.Store} store
20692      * The data source for the calendar
20693      */
20694         store : false,
20695      /**
20696      * @cfg {Number} startDay
20697      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20698      */
20699     startDay : 0,
20700     
20701     loadMask : false,
20702     
20703     header : false,
20704       
20705     getAutoCreate : function(){
20706         
20707         
20708         var fc_button = function(name, corner, style, content ) {
20709             return Roo.apply({},{
20710                 tag : 'span',
20711                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20712                          (corner.length ?
20713                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20714                             ''
20715                         ),
20716                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20717                 unselectable: 'on'
20718             });
20719         };
20720         
20721         var header = {};
20722         
20723         if(!this.header){
20724             header = {
20725                 tag : 'table',
20726                 cls : 'fc-header',
20727                 style : 'width:100%',
20728                 cn : [
20729                     {
20730                         tag: 'tr',
20731                         cn : [
20732                             {
20733                                 tag : 'td',
20734                                 cls : 'fc-header-left',
20735                                 cn : [
20736                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20737                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20738                                     { tag: 'span', cls: 'fc-header-space' },
20739                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20740
20741
20742                                 ]
20743                             },
20744
20745                             {
20746                                 tag : 'td',
20747                                 cls : 'fc-header-center',
20748                                 cn : [
20749                                     {
20750                                         tag: 'span',
20751                                         cls: 'fc-header-title',
20752                                         cn : {
20753                                             tag: 'H2',
20754                                             html : 'month / year'
20755                                         }
20756                                     }
20757
20758                                 ]
20759                             },
20760                             {
20761                                 tag : 'td',
20762                                 cls : 'fc-header-right',
20763                                 cn : [
20764                               /*      fc_button('month', 'left', '', 'month' ),
20765                                     fc_button('week', '', '', 'week' ),
20766                                     fc_button('day', 'right', '', 'day' )
20767                                 */    
20768
20769                                 ]
20770                             }
20771
20772                         ]
20773                     }
20774                 ]
20775             };
20776         }
20777         
20778         header = this.header;
20779         
20780        
20781         var cal_heads = function() {
20782             var ret = [];
20783             // fixme - handle this.
20784             
20785             for (var i =0; i < Date.dayNames.length; i++) {
20786                 var d = Date.dayNames[i];
20787                 ret.push({
20788                     tag: 'th',
20789                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20790                     html : d.substring(0,3)
20791                 });
20792                 
20793             }
20794             ret[0].cls += ' fc-first';
20795             ret[6].cls += ' fc-last';
20796             return ret;
20797         };
20798         var cal_cell = function(n) {
20799             return  {
20800                 tag: 'td',
20801                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20802                 cn : [
20803                     {
20804                         cn : [
20805                             {
20806                                 cls: 'fc-day-number',
20807                                 html: 'D'
20808                             },
20809                             {
20810                                 cls: 'fc-day-content',
20811                              
20812                                 cn : [
20813                                      {
20814                                         style: 'position: relative;' // height: 17px;
20815                                     }
20816                                 ]
20817                             }
20818                             
20819                             
20820                         ]
20821                     }
20822                 ]
20823                 
20824             }
20825         };
20826         var cal_rows = function() {
20827             
20828             var ret = [];
20829             for (var r = 0; r < 6; r++) {
20830                 var row= {
20831                     tag : 'tr',
20832                     cls : 'fc-week',
20833                     cn : []
20834                 };
20835                 
20836                 for (var i =0; i < Date.dayNames.length; i++) {
20837                     var d = Date.dayNames[i];
20838                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20839
20840                 }
20841                 row.cn[0].cls+=' fc-first';
20842                 row.cn[0].cn[0].style = 'min-height:90px';
20843                 row.cn[6].cls+=' fc-last';
20844                 ret.push(row);
20845                 
20846             }
20847             ret[0].cls += ' fc-first';
20848             ret[4].cls += ' fc-prev-last';
20849             ret[5].cls += ' fc-last';
20850             return ret;
20851             
20852         };
20853         
20854         var cal_table = {
20855             tag: 'table',
20856             cls: 'fc-border-separate',
20857             style : 'width:100%',
20858             cellspacing  : 0,
20859             cn : [
20860                 { 
20861                     tag: 'thead',
20862                     cn : [
20863                         { 
20864                             tag: 'tr',
20865                             cls : 'fc-first fc-last',
20866                             cn : cal_heads()
20867                         }
20868                     ]
20869                 },
20870                 { 
20871                     tag: 'tbody',
20872                     cn : cal_rows()
20873                 }
20874                   
20875             ]
20876         };
20877          
20878          var cfg = {
20879             cls : 'fc fc-ltr',
20880             cn : [
20881                 header,
20882                 {
20883                     cls : 'fc-content',
20884                     style : "position: relative;",
20885                     cn : [
20886                         {
20887                             cls : 'fc-view fc-view-month fc-grid',
20888                             style : 'position: relative',
20889                             unselectable : 'on',
20890                             cn : [
20891                                 {
20892                                     cls : 'fc-event-container',
20893                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20894                                 },
20895                                 cal_table
20896                             ]
20897                         }
20898                     ]
20899     
20900                 }
20901            ] 
20902             
20903         };
20904         
20905          
20906         
20907         return cfg;
20908     },
20909     
20910     
20911     initEvents : function()
20912     {
20913         if(!this.store){
20914             throw "can not find store for calendar";
20915         }
20916         
20917         var mark = {
20918             tag: "div",
20919             cls:"x-dlg-mask",
20920             style: "text-align:center",
20921             cn: [
20922                 {
20923                     tag: "div",
20924                     style: "background-color:white;width:50%;margin:250 auto",
20925                     cn: [
20926                         {
20927                             tag: "img",
20928                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20929                         },
20930                         {
20931                             tag: "span",
20932                             html: "Loading"
20933                         }
20934                         
20935                     ]
20936                 }
20937             ]
20938         };
20939         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20940         
20941         var size = this.el.select('.fc-content', true).first().getSize();
20942         this.maskEl.setSize(size.width, size.height);
20943         this.maskEl.enableDisplayMode("block");
20944         if(!this.loadMask){
20945             this.maskEl.hide();
20946         }
20947         
20948         this.store = Roo.factory(this.store, Roo.data);
20949         this.store.on('load', this.onLoad, this);
20950         this.store.on('beforeload', this.onBeforeLoad, this);
20951         
20952         this.resize();
20953         
20954         this.cells = this.el.select('.fc-day',true);
20955         //Roo.log(this.cells);
20956         this.textNodes = this.el.query('.fc-day-number');
20957         this.cells.addClassOnOver('fc-state-hover');
20958         
20959         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20960         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20961         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20962         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20963         
20964         this.on('monthchange', this.onMonthChange, this);
20965         
20966         this.update(new Date().clearTime());
20967     },
20968     
20969     resize : function() {
20970         var sz  = this.el.getSize();
20971         
20972         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20973         this.el.select('.fc-day-content div',true).setHeight(34);
20974     },
20975     
20976     
20977     // private
20978     showPrevMonth : function(e){
20979         this.update(this.activeDate.add("mo", -1));
20980     },
20981     showToday : function(e){
20982         this.update(new Date().clearTime());
20983     },
20984     // private
20985     showNextMonth : function(e){
20986         this.update(this.activeDate.add("mo", 1));
20987     },
20988
20989     // private
20990     showPrevYear : function(){
20991         this.update(this.activeDate.add("y", -1));
20992     },
20993
20994     // private
20995     showNextYear : function(){
20996         this.update(this.activeDate.add("y", 1));
20997     },
20998
20999     
21000    // private
21001     update : function(date)
21002     {
21003         var vd = this.activeDate;
21004         this.activeDate = date;
21005 //        if(vd && this.el){
21006 //            var t = date.getTime();
21007 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21008 //                Roo.log('using add remove');
21009 //                
21010 //                this.fireEvent('monthchange', this, date);
21011 //                
21012 //                this.cells.removeClass("fc-state-highlight");
21013 //                this.cells.each(function(c){
21014 //                   if(c.dateValue == t){
21015 //                       c.addClass("fc-state-highlight");
21016 //                       setTimeout(function(){
21017 //                            try{c.dom.firstChild.focus();}catch(e){}
21018 //                       }, 50);
21019 //                       return false;
21020 //                   }
21021 //                   return true;
21022 //                });
21023 //                return;
21024 //            }
21025 //        }
21026         
21027         var days = date.getDaysInMonth();
21028         
21029         var firstOfMonth = date.getFirstDateOfMonth();
21030         var startingPos = firstOfMonth.getDay()-this.startDay;
21031         
21032         if(startingPos < this.startDay){
21033             startingPos += 7;
21034         }
21035         
21036         var pm = date.add(Date.MONTH, -1);
21037         var prevStart = pm.getDaysInMonth()-startingPos;
21038 //        
21039         this.cells = this.el.select('.fc-day',true);
21040         this.textNodes = this.el.query('.fc-day-number');
21041         this.cells.addClassOnOver('fc-state-hover');
21042         
21043         var cells = this.cells.elements;
21044         var textEls = this.textNodes;
21045         
21046         Roo.each(cells, function(cell){
21047             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21048         });
21049         
21050         days += startingPos;
21051
21052         // convert everything to numbers so it's fast
21053         var day = 86400000;
21054         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21055         //Roo.log(d);
21056         //Roo.log(pm);
21057         //Roo.log(prevStart);
21058         
21059         var today = new Date().clearTime().getTime();
21060         var sel = date.clearTime().getTime();
21061         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21062         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21063         var ddMatch = this.disabledDatesRE;
21064         var ddText = this.disabledDatesText;
21065         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21066         var ddaysText = this.disabledDaysText;
21067         var format = this.format;
21068         
21069         var setCellClass = function(cal, cell){
21070             cell.row = 0;
21071             cell.events = [];
21072             cell.more = [];
21073             //Roo.log('set Cell Class');
21074             cell.title = "";
21075             var t = d.getTime();
21076             
21077             //Roo.log(d);
21078             
21079             cell.dateValue = t;
21080             if(t == today){
21081                 cell.className += " fc-today";
21082                 cell.className += " fc-state-highlight";
21083                 cell.title = cal.todayText;
21084             }
21085             if(t == sel){
21086                 // disable highlight in other month..
21087                 //cell.className += " fc-state-highlight";
21088                 
21089             }
21090             // disabling
21091             if(t < min) {
21092                 cell.className = " fc-state-disabled";
21093                 cell.title = cal.minText;
21094                 return;
21095             }
21096             if(t > max) {
21097                 cell.className = " fc-state-disabled";
21098                 cell.title = cal.maxText;
21099                 return;
21100             }
21101             if(ddays){
21102                 if(ddays.indexOf(d.getDay()) != -1){
21103                     cell.title = ddaysText;
21104                     cell.className = " fc-state-disabled";
21105                 }
21106             }
21107             if(ddMatch && format){
21108                 var fvalue = d.dateFormat(format);
21109                 if(ddMatch.test(fvalue)){
21110                     cell.title = ddText.replace("%0", fvalue);
21111                     cell.className = " fc-state-disabled";
21112                 }
21113             }
21114             
21115             if (!cell.initialClassName) {
21116                 cell.initialClassName = cell.dom.className;
21117             }
21118             
21119             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21120         };
21121
21122         var i = 0;
21123         
21124         for(; i < startingPos; i++) {
21125             textEls[i].innerHTML = (++prevStart);
21126             d.setDate(d.getDate()+1);
21127             
21128             cells[i].className = "fc-past fc-other-month";
21129             setCellClass(this, cells[i]);
21130         }
21131         
21132         var intDay = 0;
21133         
21134         for(; i < days; i++){
21135             intDay = i - startingPos + 1;
21136             textEls[i].innerHTML = (intDay);
21137             d.setDate(d.getDate()+1);
21138             
21139             cells[i].className = ''; // "x-date-active";
21140             setCellClass(this, cells[i]);
21141         }
21142         var extraDays = 0;
21143         
21144         for(; i < 42; i++) {
21145             textEls[i].innerHTML = (++extraDays);
21146             d.setDate(d.getDate()+1);
21147             
21148             cells[i].className = "fc-future fc-other-month";
21149             setCellClass(this, cells[i]);
21150         }
21151         
21152         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21153         
21154         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21155         
21156         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21157         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21158         
21159         if(totalRows != 6){
21160             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21161             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21162         }
21163         
21164         this.fireEvent('monthchange', this, date);
21165         
21166         
21167         /*
21168         if(!this.internalRender){
21169             var main = this.el.dom.firstChild;
21170             var w = main.offsetWidth;
21171             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21172             Roo.fly(main).setWidth(w);
21173             this.internalRender = true;
21174             // opera does not respect the auto grow header center column
21175             // then, after it gets a width opera refuses to recalculate
21176             // without a second pass
21177             if(Roo.isOpera && !this.secondPass){
21178                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21179                 this.secondPass = true;
21180                 this.update.defer(10, this, [date]);
21181             }
21182         }
21183         */
21184         
21185     },
21186     
21187     findCell : function(dt) {
21188         dt = dt.clearTime().getTime();
21189         var ret = false;
21190         this.cells.each(function(c){
21191             //Roo.log("check " +c.dateValue + '?=' + dt);
21192             if(c.dateValue == dt){
21193                 ret = c;
21194                 return false;
21195             }
21196             return true;
21197         });
21198         
21199         return ret;
21200     },
21201     
21202     findCells : function(ev) {
21203         var s = ev.start.clone().clearTime().getTime();
21204        // Roo.log(s);
21205         var e= ev.end.clone().clearTime().getTime();
21206        // Roo.log(e);
21207         var ret = [];
21208         this.cells.each(function(c){
21209              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21210             
21211             if(c.dateValue > e){
21212                 return ;
21213             }
21214             if(c.dateValue < s){
21215                 return ;
21216             }
21217             ret.push(c);
21218         });
21219         
21220         return ret;    
21221     },
21222     
21223 //    findBestRow: function(cells)
21224 //    {
21225 //        var ret = 0;
21226 //        
21227 //        for (var i =0 ; i < cells.length;i++) {
21228 //            ret  = Math.max(cells[i].rows || 0,ret);
21229 //        }
21230 //        return ret;
21231 //        
21232 //    },
21233     
21234     
21235     addItem : function(ev)
21236     {
21237         // look for vertical location slot in
21238         var cells = this.findCells(ev);
21239         
21240 //        ev.row = this.findBestRow(cells);
21241         
21242         // work out the location.
21243         
21244         var crow = false;
21245         var rows = [];
21246         for(var i =0; i < cells.length; i++) {
21247             
21248             cells[i].row = cells[0].row;
21249             
21250             if(i == 0){
21251                 cells[i].row = cells[i].row + 1;
21252             }
21253             
21254             if (!crow) {
21255                 crow = {
21256                     start : cells[i],
21257                     end :  cells[i]
21258                 };
21259                 continue;
21260             }
21261             if (crow.start.getY() == cells[i].getY()) {
21262                 // on same row.
21263                 crow.end = cells[i];
21264                 continue;
21265             }
21266             // different row.
21267             rows.push(crow);
21268             crow = {
21269                 start: cells[i],
21270                 end : cells[i]
21271             };
21272             
21273         }
21274         
21275         rows.push(crow);
21276         ev.els = [];
21277         ev.rows = rows;
21278         ev.cells = cells;
21279         
21280         cells[0].events.push(ev);
21281         
21282         this.calevents.push(ev);
21283     },
21284     
21285     clearEvents: function() {
21286         
21287         if(!this.calevents){
21288             return;
21289         }
21290         
21291         Roo.each(this.cells.elements, function(c){
21292             c.row = 0;
21293             c.events = [];
21294             c.more = [];
21295         });
21296         
21297         Roo.each(this.calevents, function(e) {
21298             Roo.each(e.els, function(el) {
21299                 el.un('mouseenter' ,this.onEventEnter, this);
21300                 el.un('mouseleave' ,this.onEventLeave, this);
21301                 el.remove();
21302             },this);
21303         },this);
21304         
21305         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21306             e.remove();
21307         });
21308         
21309     },
21310     
21311     renderEvents: function()
21312     {   
21313         var _this = this;
21314         
21315         this.cells.each(function(c) {
21316             
21317             if(c.row < 5){
21318                 return;
21319             }
21320             
21321             var ev = c.events;
21322             
21323             var r = 4;
21324             if(c.row != c.events.length){
21325                 r = 4 - (4 - (c.row - c.events.length));
21326             }
21327             
21328             c.events = ev.slice(0, r);
21329             c.more = ev.slice(r);
21330             
21331             if(c.more.length && c.more.length == 1){
21332                 c.events.push(c.more.pop());
21333             }
21334             
21335             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21336             
21337         });
21338             
21339         this.cells.each(function(c) {
21340             
21341             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21342             
21343             
21344             for (var e = 0; e < c.events.length; e++){
21345                 var ev = c.events[e];
21346                 var rows = ev.rows;
21347                 
21348                 for(var i = 0; i < rows.length; i++) {
21349                 
21350                     // how many rows should it span..
21351
21352                     var  cfg = {
21353                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21354                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21355
21356                         unselectable : "on",
21357                         cn : [
21358                             {
21359                                 cls: 'fc-event-inner',
21360                                 cn : [
21361     //                                {
21362     //                                  tag:'span',
21363     //                                  cls: 'fc-event-time',
21364     //                                  html : cells.length > 1 ? '' : ev.time
21365     //                                },
21366                                     {
21367                                       tag:'span',
21368                                       cls: 'fc-event-title',
21369                                       html : String.format('{0}', ev.title)
21370                                     }
21371
21372
21373                                 ]
21374                             },
21375                             {
21376                                 cls: 'ui-resizable-handle ui-resizable-e',
21377                                 html : '&nbsp;&nbsp;&nbsp'
21378                             }
21379
21380                         ]
21381                     };
21382
21383                     if (i == 0) {
21384                         cfg.cls += ' fc-event-start';
21385                     }
21386                     if ((i+1) == rows.length) {
21387                         cfg.cls += ' fc-event-end';
21388                     }
21389
21390                     var ctr = _this.el.select('.fc-event-container',true).first();
21391                     var cg = ctr.createChild(cfg);
21392
21393                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21394                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21395
21396                     var r = (c.more.length) ? 1 : 0;
21397                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21398                     cg.setWidth(ebox.right - sbox.x -2);
21399
21400                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21401                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21402                     cg.on('click', _this.onEventClick, _this, ev);
21403
21404                     ev.els.push(cg);
21405                     
21406                 }
21407                 
21408             }
21409             
21410             
21411             if(c.more.length){
21412                 var  cfg = {
21413                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21414                     style : 'position: absolute',
21415                     unselectable : "on",
21416                     cn : [
21417                         {
21418                             cls: 'fc-event-inner',
21419                             cn : [
21420                                 {
21421                                   tag:'span',
21422                                   cls: 'fc-event-title',
21423                                   html : 'More'
21424                                 }
21425
21426
21427                             ]
21428                         },
21429                         {
21430                             cls: 'ui-resizable-handle ui-resizable-e',
21431                             html : '&nbsp;&nbsp;&nbsp'
21432                         }
21433
21434                     ]
21435                 };
21436
21437                 var ctr = _this.el.select('.fc-event-container',true).first();
21438                 var cg = ctr.createChild(cfg);
21439
21440                 var sbox = c.select('.fc-day-content',true).first().getBox();
21441                 var ebox = c.select('.fc-day-content',true).first().getBox();
21442                 //Roo.log(cg);
21443                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21444                 cg.setWidth(ebox.right - sbox.x -2);
21445
21446                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21447                 
21448             }
21449             
21450         });
21451         
21452         
21453         
21454     },
21455     
21456     onEventEnter: function (e, el,event,d) {
21457         this.fireEvent('evententer', this, el, event);
21458     },
21459     
21460     onEventLeave: function (e, el,event,d) {
21461         this.fireEvent('eventleave', this, el, event);
21462     },
21463     
21464     onEventClick: function (e, el,event,d) {
21465         this.fireEvent('eventclick', this, el, event);
21466     },
21467     
21468     onMonthChange: function () {
21469         this.store.load();
21470     },
21471     
21472     onMoreEventClick: function(e, el, more)
21473     {
21474         var _this = this;
21475         
21476         this.calpopover.placement = 'right';
21477         this.calpopover.setTitle('More');
21478         
21479         this.calpopover.setContent('');
21480         
21481         var ctr = this.calpopover.el.select('.popover-content', true).first();
21482         
21483         Roo.each(more, function(m){
21484             var cfg = {
21485                 cls : 'fc-event-hori fc-event-draggable',
21486                 html : m.title
21487             };
21488             var cg = ctr.createChild(cfg);
21489             
21490             cg.on('click', _this.onEventClick, _this, m);
21491         });
21492         
21493         this.calpopover.show(el);
21494         
21495         
21496     },
21497     
21498     onLoad: function () 
21499     {   
21500         this.calevents = [];
21501         var cal = this;
21502         
21503         if(this.store.getCount() > 0){
21504             this.store.data.each(function(d){
21505                cal.addItem({
21506                     id : d.data.id,
21507                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21508                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21509                     time : d.data.start_time,
21510                     title : d.data.title,
21511                     description : d.data.description,
21512                     venue : d.data.venue
21513                 });
21514             });
21515         }
21516         
21517         this.renderEvents();
21518         
21519         if(this.calevents.length && this.loadMask){
21520             this.maskEl.hide();
21521         }
21522     },
21523     
21524     onBeforeLoad: function()
21525     {
21526         this.clearEvents();
21527         if(this.loadMask){
21528             this.maskEl.show();
21529         }
21530     }
21531 });
21532
21533  
21534  /*
21535  * - LGPL
21536  *
21537  * element
21538  * 
21539  */
21540
21541 /**
21542  * @class Roo.bootstrap.Popover
21543  * @extends Roo.bootstrap.Component
21544  * @parent none builder
21545  * @children Roo.bootstrap.Component
21546  * Bootstrap Popover class
21547  * @cfg {String} html contents of the popover   (or false to use children..)
21548  * @cfg {String} title of popover (or false to hide)
21549  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21550  * @cfg {String} trigger click || hover (or false to trigger manually)
21551  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21552  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21553  *      - if false and it has a 'parent' then it will be automatically added to that element
21554  *      - if string - Roo.get  will be called 
21555  * @cfg {Number} delay - delay before showing
21556  
21557  * @constructor
21558  * Create a new Popover
21559  * @param {Object} config The config object
21560  */
21561
21562 Roo.bootstrap.Popover = function(config){
21563     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21564     
21565     this.addEvents({
21566         // raw events
21567          /**
21568          * @event show
21569          * After the popover show
21570          * 
21571          * @param {Roo.bootstrap.Popover} this
21572          */
21573         "show" : true,
21574         /**
21575          * @event hide
21576          * After the popover hide
21577          * 
21578          * @param {Roo.bootstrap.Popover} this
21579          */
21580         "hide" : true
21581     });
21582 };
21583
21584 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21585     
21586     title: false,
21587     html: false,
21588     
21589     placement : 'right',
21590     trigger : 'hover', // hover
21591     modal : false,
21592     delay : 0,
21593     
21594     over: false,
21595     
21596     can_build_overlaid : false,
21597     
21598     maskEl : false, // the mask element
21599     headerEl : false,
21600     contentEl : false,
21601     alignEl : false, // when show is called with an element - this get's stored.
21602     
21603     getChildContainer : function()
21604     {
21605         return this.contentEl;
21606         
21607     },
21608     getPopoverHeader : function()
21609     {
21610         this.title = true; // flag not to hide it..
21611         this.headerEl.addClass('p-0');
21612         return this.headerEl
21613     },
21614     
21615     
21616     getAutoCreate : function(){
21617          
21618         var cfg = {
21619            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21620            style: 'display:block',
21621            cn : [
21622                 {
21623                     cls : 'arrow'
21624                 },
21625                 {
21626                     cls : 'popover-inner ',
21627                     cn : [
21628                         {
21629                             tag: 'h3',
21630                             cls: 'popover-title popover-header',
21631                             html : this.title === false ? '' : this.title
21632                         },
21633                         {
21634                             cls : 'popover-content popover-body '  + (this.cls || ''),
21635                             html : this.html || ''
21636                         }
21637                     ]
21638                     
21639                 }
21640            ]
21641         };
21642         
21643         return cfg;
21644     },
21645     /**
21646      * @param {string} the title
21647      */
21648     setTitle: function(str)
21649     {
21650         this.title = str;
21651         if (this.el) {
21652             this.headerEl.dom.innerHTML = str;
21653         }
21654         
21655     },
21656     /**
21657      * @param {string} the body content
21658      */
21659     setContent: function(str)
21660     {
21661         this.html = str;
21662         if (this.contentEl) {
21663             this.contentEl.dom.innerHTML = str;
21664         }
21665         
21666     },
21667     // as it get's added to the bottom of the page.
21668     onRender : function(ct, position)
21669     {
21670         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21671         
21672         
21673         
21674         if(!this.el){
21675             var cfg = Roo.apply({},  this.getAutoCreate());
21676             cfg.id = Roo.id();
21677             
21678             if (this.cls) {
21679                 cfg.cls += ' ' + this.cls;
21680             }
21681             if (this.style) {
21682                 cfg.style = this.style;
21683             }
21684             //Roo.log("adding to ");
21685             this.el = Roo.get(document.body).createChild(cfg, position);
21686 //            Roo.log(this.el);
21687         }
21688         
21689         this.contentEl = this.el.select('.popover-content',true).first();
21690         this.headerEl =  this.el.select('.popover-title',true).first();
21691         
21692         var nitems = [];
21693         if(typeof(this.items) != 'undefined'){
21694             var items = this.items;
21695             delete this.items;
21696
21697             for(var i =0;i < items.length;i++) {
21698                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21699             }
21700         }
21701
21702         this.items = nitems;
21703         
21704         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21705         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21706         
21707         
21708         
21709         this.initEvents();
21710     },
21711     
21712     resizeMask : function()
21713     {
21714         this.maskEl.setSize(
21715             Roo.lib.Dom.getViewWidth(true),
21716             Roo.lib.Dom.getViewHeight(true)
21717         );
21718     },
21719     
21720     initEvents : function()
21721     {
21722         
21723         if (!this.modal) { 
21724             Roo.bootstrap.Popover.register(this);
21725         }
21726          
21727         this.arrowEl = this.el.select('.arrow',true).first();
21728         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21729         this.el.enableDisplayMode('block');
21730         this.el.hide();
21731  
21732         
21733         if (this.over === false && !this.parent()) {
21734             return; 
21735         }
21736         if (this.triggers === false) {
21737             return;
21738         }
21739          
21740         // support parent
21741         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21742         var triggers = this.trigger ? this.trigger.split(' ') : [];
21743         Roo.each(triggers, function(trigger) {
21744         
21745             if (trigger == 'click') {
21746                 on_el.on('click', this.toggle, this);
21747             } else if (trigger != 'manual') {
21748                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21749                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21750       
21751                 on_el.on(eventIn  ,this.enter, this);
21752                 on_el.on(eventOut, this.leave, this);
21753             }
21754         }, this);
21755     },
21756     
21757     
21758     // private
21759     timeout : null,
21760     hoverState : null,
21761     
21762     toggle : function () {
21763         this.hoverState == 'in' ? this.leave() : this.enter();
21764     },
21765     
21766     enter : function () {
21767         
21768         clearTimeout(this.timeout);
21769     
21770         this.hoverState = 'in';
21771     
21772         if (!this.delay || !this.delay.show) {
21773             this.show();
21774             return;
21775         }
21776         var _t = this;
21777         this.timeout = setTimeout(function () {
21778             if (_t.hoverState == 'in') {
21779                 _t.show();
21780             }
21781         }, this.delay.show)
21782     },
21783     
21784     leave : function() {
21785         clearTimeout(this.timeout);
21786     
21787         this.hoverState = 'out';
21788     
21789         if (!this.delay || !this.delay.hide) {
21790             this.hide();
21791             return;
21792         }
21793         var _t = this;
21794         this.timeout = setTimeout(function () {
21795             if (_t.hoverState == 'out') {
21796                 _t.hide();
21797             }
21798         }, this.delay.hide)
21799     },
21800     
21801     /**
21802      * update the position of the dialog
21803      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21804      * 
21805      *
21806      */
21807     
21808     doAlign : function()
21809     {
21810         
21811         if (this.alignEl) {
21812             this.updatePosition(this.placement, true);
21813              
21814         } else {
21815             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21816             var es = this.el.getSize();
21817             var x = Roo.lib.Dom.getViewWidth()/2;
21818             var y = Roo.lib.Dom.getViewHeight()/2;
21819             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21820             
21821         }
21822
21823          
21824          
21825         
21826         
21827     },
21828     
21829     /**
21830      * Show the popover
21831      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21832      * @param {string} (left|right|top|bottom) position
21833      */
21834     show : function (on_el, placement)
21835     {
21836         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21837         on_el = on_el || false; // default to false
21838          
21839         if (!on_el) {
21840             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21841                 on_el = this.parent().el;
21842             } else if (this.over) {
21843                 on_el = Roo.get(this.over);
21844             }
21845             
21846         }
21847         
21848         this.alignEl = Roo.get( on_el );
21849
21850         if (!this.el) {
21851             this.render(document.body);
21852         }
21853         
21854         
21855          
21856         
21857         if (this.title === false) {
21858             this.headerEl.hide();
21859         }
21860         
21861        
21862         this.el.show();
21863         this.el.dom.style.display = 'block';
21864          
21865         this.doAlign();
21866         
21867         //var arrow = this.el.select('.arrow',true).first();
21868         //arrow.set(align[2], 
21869         
21870         this.el.addClass('in');
21871         
21872          
21873         
21874         this.hoverState = 'in';
21875         
21876         if (this.modal) {
21877             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21878             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21879             this.maskEl.dom.style.display = 'block';
21880             this.maskEl.addClass('show');
21881         }
21882         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21883  
21884         this.fireEvent('show', this);
21885         
21886     },
21887     /**
21888      * fire this manually after loading a grid in the table for example
21889      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21890      * @param {Boolean} try and move it if we cant get right position.
21891      */
21892     updatePosition : function(placement, try_move)
21893     {
21894         // allow for calling with no parameters
21895         placement = placement   ? placement :  this.placement;
21896         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21897         
21898         this.el.removeClass([
21899             'fade','top','bottom', 'left', 'right','in',
21900             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21901         ]);
21902         this.el.addClass(placement + ' bs-popover-' + placement);
21903         
21904         if (!this.alignEl ) {
21905             return false;
21906         }
21907         
21908         switch (placement) {
21909             case 'right':
21910                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21911                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21912                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21913                     //normal display... or moved up/down.
21914                     this.el.setXY(offset);
21915                     var xy = this.alignEl.getAnchorXY('tr', false);
21916                     xy[0]+=2;xy[1]+=5;
21917                     this.arrowEl.setXY(xy);
21918                     return true;
21919                 }
21920                 // continue through...
21921                 return this.updatePosition('left', false);
21922                 
21923             
21924             case 'left':
21925                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21926                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21927                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21928                     //normal display... or moved up/down.
21929                     this.el.setXY(offset);
21930                     var xy = this.alignEl.getAnchorXY('tl', false);
21931                     xy[0]-=10;xy[1]+=5; // << fix me
21932                     this.arrowEl.setXY(xy);
21933                     return true;
21934                 }
21935                 // call self...
21936                 return this.updatePosition('right', false);
21937             
21938             case 'top':
21939                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21940                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21941                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21942                     //normal display... or moved up/down.
21943                     this.el.setXY(offset);
21944                     var xy = this.alignEl.getAnchorXY('t', false);
21945                     xy[1]-=10; // << fix me
21946                     this.arrowEl.setXY(xy);
21947                     return true;
21948                 }
21949                 // fall through
21950                return this.updatePosition('bottom', false);
21951             
21952             case 'bottom':
21953                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21954                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21955                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21956                     //normal display... or moved up/down.
21957                     this.el.setXY(offset);
21958                     var xy = this.alignEl.getAnchorXY('b', false);
21959                      xy[1]+=2; // << fix me
21960                     this.arrowEl.setXY(xy);
21961                     return true;
21962                 }
21963                 // fall through
21964                 return this.updatePosition('top', false);
21965                 
21966             
21967         }
21968         
21969         
21970         return false;
21971     },
21972     
21973     hide : function()
21974     {
21975         this.el.setXY([0,0]);
21976         this.el.removeClass('in');
21977         this.el.hide();
21978         this.hoverState = null;
21979         this.maskEl.hide(); // always..
21980         this.fireEvent('hide', this);
21981     }
21982     
21983 });
21984
21985
21986 Roo.apply(Roo.bootstrap.Popover, {
21987
21988     alignment : {
21989         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21990         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21991         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21992         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21993     },
21994     
21995     zIndex : 20001,
21996
21997     clickHander : false,
21998     
21999     
22000
22001     onMouseDown : function(e)
22002     {
22003         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22004             /// what is nothing is showing..
22005             this.hideAll();
22006         }
22007          
22008     },
22009     
22010     
22011     popups : [],
22012     
22013     register : function(popup)
22014     {
22015         if (!Roo.bootstrap.Popover.clickHandler) {
22016             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22017         }
22018         // hide other popups.
22019         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22020         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22021         this.hideAll(); //<< why?
22022         //this.popups.push(popup);
22023     },
22024     hideAll : function()
22025     {
22026         this.popups.forEach(function(p) {
22027             p.hide();
22028         });
22029     },
22030     onShow : function() {
22031         Roo.bootstrap.Popover.popups.push(this);
22032     },
22033     onHide : function() {
22034         Roo.bootstrap.Popover.popups.remove(this);
22035     } 
22036
22037 });
22038 /**
22039  * @class Roo.bootstrap.PopoverNav
22040  * @extends Roo.bootstrap.nav.Simplebar
22041  * @parent Roo.bootstrap.Popover
22042  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22043  * @licence LGPL
22044  * Bootstrap Popover header navigation class
22045  * FIXME? should this go under nav?
22046  *
22047  * 
22048  * @constructor
22049  * Create a new Popover Header Navigation 
22050  * @param {Object} config The config object
22051  */
22052
22053 Roo.bootstrap.PopoverNav = function(config){
22054     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22055 };
22056
22057 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22058     
22059     
22060     container_method : 'getPopoverHeader' 
22061     
22062      
22063     
22064     
22065    
22066 });
22067
22068  
22069
22070  /*
22071  * - LGPL
22072  *
22073  * Progress
22074  * 
22075  */
22076
22077 /**
22078  * @class Roo.bootstrap.Progress
22079  * @extends Roo.bootstrap.Component
22080  * @children Roo.bootstrap.ProgressBar
22081  * Bootstrap Progress class
22082  * @cfg {Boolean} striped striped of the progress bar
22083  * @cfg {Boolean} active animated of the progress bar
22084  * 
22085  * 
22086  * @constructor
22087  * Create a new Progress
22088  * @param {Object} config The config object
22089  */
22090
22091 Roo.bootstrap.Progress = function(config){
22092     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22093 };
22094
22095 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22096     
22097     striped : false,
22098     active: false,
22099     
22100     getAutoCreate : function(){
22101         var cfg = {
22102             tag: 'div',
22103             cls: 'progress'
22104         };
22105         
22106         
22107         if(this.striped){
22108             cfg.cls += ' progress-striped';
22109         }
22110       
22111         if(this.active){
22112             cfg.cls += ' active';
22113         }
22114         
22115         
22116         return cfg;
22117     }
22118    
22119 });
22120
22121  
22122
22123  /*
22124  * - LGPL
22125  *
22126  * ProgressBar
22127  * 
22128  */
22129
22130 /**
22131  * @class Roo.bootstrap.ProgressBar
22132  * @extends Roo.bootstrap.Component
22133  * Bootstrap ProgressBar class
22134  * @cfg {Number} aria_valuenow aria-value now
22135  * @cfg {Number} aria_valuemin aria-value min
22136  * @cfg {Number} aria_valuemax aria-value max
22137  * @cfg {String} label label for the progress bar
22138  * @cfg {String} panel (success | info | warning | danger )
22139  * @cfg {String} role role of the progress bar
22140  * @cfg {String} sr_only text
22141  * 
22142  * 
22143  * @constructor
22144  * Create a new ProgressBar
22145  * @param {Object} config The config object
22146  */
22147
22148 Roo.bootstrap.ProgressBar = function(config){
22149     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22150 };
22151
22152 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22153     
22154     aria_valuenow : 0,
22155     aria_valuemin : 0,
22156     aria_valuemax : 100,
22157     label : false,
22158     panel : false,
22159     role : false,
22160     sr_only: false,
22161     
22162     getAutoCreate : function()
22163     {
22164         
22165         var cfg = {
22166             tag: 'div',
22167             cls: 'progress-bar',
22168             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22169         };
22170         
22171         if(this.sr_only){
22172             cfg.cn = {
22173                 tag: 'span',
22174                 cls: 'sr-only',
22175                 html: this.sr_only
22176             }
22177         }
22178         
22179         if(this.role){
22180             cfg.role = this.role;
22181         }
22182         
22183         if(this.aria_valuenow){
22184             cfg['aria-valuenow'] = this.aria_valuenow;
22185         }
22186         
22187         if(this.aria_valuemin){
22188             cfg['aria-valuemin'] = this.aria_valuemin;
22189         }
22190         
22191         if(this.aria_valuemax){
22192             cfg['aria-valuemax'] = this.aria_valuemax;
22193         }
22194         
22195         if(this.label && !this.sr_only){
22196             cfg.html = this.label;
22197         }
22198         
22199         if(this.panel){
22200             cfg.cls += ' progress-bar-' + this.panel;
22201         }
22202         
22203         return cfg;
22204     },
22205     
22206     update : function(aria_valuenow)
22207     {
22208         this.aria_valuenow = aria_valuenow;
22209         
22210         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22211     }
22212    
22213 });
22214
22215  
22216
22217  /**
22218  * @class Roo.bootstrap.TabGroup
22219  * @extends Roo.bootstrap.Column
22220  * @children Roo.bootstrap.TabPanel
22221  * Bootstrap Column class
22222  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22223  * @cfg {Boolean} carousel true to make the group behave like a carousel
22224  * @cfg {Boolean} bullets show bullets for the panels
22225  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22226  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22227  * @cfg {Boolean} showarrow (true|false) show arrow default true
22228  * 
22229  * @constructor
22230  * Create a new TabGroup
22231  * @param {Object} config The config object
22232  */
22233
22234 Roo.bootstrap.TabGroup = function(config){
22235     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22236     if (!this.navId) {
22237         this.navId = Roo.id();
22238     }
22239     this.tabs = [];
22240     Roo.bootstrap.TabGroup.register(this);
22241     
22242 };
22243
22244 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22245     
22246     carousel : false,
22247     transition : false,
22248     bullets : 0,
22249     timer : 0,
22250     autoslide : false,
22251     slideFn : false,
22252     slideOnTouch : false,
22253     showarrow : true,
22254     
22255     getAutoCreate : function()
22256     {
22257         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22258         
22259         cfg.cls += ' tab-content';
22260         
22261         if (this.carousel) {
22262             cfg.cls += ' carousel slide';
22263             
22264             cfg.cn = [{
22265                cls : 'carousel-inner',
22266                cn : []
22267             }];
22268         
22269             if(this.bullets  && !Roo.isTouch){
22270                 
22271                 var bullets = {
22272                     cls : 'carousel-bullets',
22273                     cn : []
22274                 };
22275                
22276                 if(this.bullets_cls){
22277                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22278                 }
22279                 
22280                 bullets.cn.push({
22281                     cls : 'clear'
22282                 });
22283                 
22284                 cfg.cn[0].cn.push(bullets);
22285             }
22286             
22287             if(this.showarrow){
22288                 cfg.cn[0].cn.push({
22289                     tag : 'div',
22290                     class : 'carousel-arrow',
22291                     cn : [
22292                         {
22293                             tag : 'div',
22294                             class : 'carousel-prev',
22295                             cn : [
22296                                 {
22297                                     tag : 'i',
22298                                     class : 'fa fa-chevron-left'
22299                                 }
22300                             ]
22301                         },
22302                         {
22303                             tag : 'div',
22304                             class : 'carousel-next',
22305                             cn : [
22306                                 {
22307                                     tag : 'i',
22308                                     class : 'fa fa-chevron-right'
22309                                 }
22310                             ]
22311                         }
22312                     ]
22313                 });
22314             }
22315             
22316         }
22317         
22318         return cfg;
22319     },
22320     
22321     initEvents:  function()
22322     {
22323 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22324 //            this.el.on("touchstart", this.onTouchStart, this);
22325 //        }
22326         
22327         if(this.autoslide){
22328             var _this = this;
22329             
22330             this.slideFn = window.setInterval(function() {
22331                 _this.showPanelNext();
22332             }, this.timer);
22333         }
22334         
22335         if(this.showarrow){
22336             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22337             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22338         }
22339         
22340         
22341     },
22342     
22343 //    onTouchStart : function(e, el, o)
22344 //    {
22345 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22346 //            return;
22347 //        }
22348 //        
22349 //        this.showPanelNext();
22350 //    },
22351     
22352     
22353     getChildContainer : function()
22354     {
22355         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22356     },
22357     
22358     /**
22359     * register a Navigation item
22360     * @param {Roo.bootstrap.nav.Item} the navitem to add
22361     */
22362     register : function(item)
22363     {
22364         this.tabs.push( item);
22365         item.navId = this.navId; // not really needed..
22366         this.addBullet();
22367     
22368     },
22369     
22370     getActivePanel : function()
22371     {
22372         var r = false;
22373         Roo.each(this.tabs, function(t) {
22374             if (t.active) {
22375                 r = t;
22376                 return false;
22377             }
22378             return null;
22379         });
22380         return r;
22381         
22382     },
22383     getPanelByName : function(n)
22384     {
22385         var r = false;
22386         Roo.each(this.tabs, function(t) {
22387             if (t.tabId == n) {
22388                 r = t;
22389                 return false;
22390             }
22391             return null;
22392         });
22393         return r;
22394     },
22395     indexOfPanel : function(p)
22396     {
22397         var r = false;
22398         Roo.each(this.tabs, function(t,i) {
22399             if (t.tabId == p.tabId) {
22400                 r = i;
22401                 return false;
22402             }
22403             return null;
22404         });
22405         return r;
22406     },
22407     /**
22408      * show a specific panel
22409      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22410      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22411      */
22412     showPanel : function (pan)
22413     {
22414         if(this.transition || typeof(pan) == 'undefined'){
22415             Roo.log("waiting for the transitionend");
22416             return false;
22417         }
22418         
22419         if (typeof(pan) == 'number') {
22420             pan = this.tabs[pan];
22421         }
22422         
22423         if (typeof(pan) == 'string') {
22424             pan = this.getPanelByName(pan);
22425         }
22426         
22427         var cur = this.getActivePanel();
22428         
22429         if(!pan || !cur){
22430             Roo.log('pan or acitve pan is undefined');
22431             return false;
22432         }
22433         
22434         if (pan.tabId == this.getActivePanel().tabId) {
22435             return true;
22436         }
22437         
22438         if (false === cur.fireEvent('beforedeactivate')) {
22439             return false;
22440         }
22441         
22442         if(this.bullets > 0 && !Roo.isTouch){
22443             this.setActiveBullet(this.indexOfPanel(pan));
22444         }
22445         
22446         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22447             
22448             //class="carousel-item carousel-item-next carousel-item-left"
22449             
22450             this.transition = true;
22451             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22452             var lr = dir == 'next' ? 'left' : 'right';
22453             pan.el.addClass(dir); // or prev
22454             pan.el.addClass('carousel-item-' + dir); // or prev
22455             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22456             cur.el.addClass(lr); // or right
22457             pan.el.addClass(lr);
22458             cur.el.addClass('carousel-item-' +lr); // or right
22459             pan.el.addClass('carousel-item-' +lr);
22460             
22461             
22462             var _this = this;
22463             cur.el.on('transitionend', function() {
22464                 Roo.log("trans end?");
22465                 
22466                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22467                 pan.setActive(true);
22468                 
22469                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22470                 cur.setActive(false);
22471                 
22472                 _this.transition = false;
22473                 
22474             }, this, { single:  true } );
22475             
22476             return true;
22477         }
22478         
22479         cur.setActive(false);
22480         pan.setActive(true);
22481         
22482         return true;
22483         
22484     },
22485     showPanelNext : function()
22486     {
22487         var i = this.indexOfPanel(this.getActivePanel());
22488         
22489         if (i >= this.tabs.length - 1 && !this.autoslide) {
22490             return;
22491         }
22492         
22493         if (i >= this.tabs.length - 1 && this.autoslide) {
22494             i = -1;
22495         }
22496         
22497         this.showPanel(this.tabs[i+1]);
22498     },
22499     
22500     showPanelPrev : function()
22501     {
22502         var i = this.indexOfPanel(this.getActivePanel());
22503         
22504         if (i  < 1 && !this.autoslide) {
22505             return;
22506         }
22507         
22508         if (i < 1 && this.autoslide) {
22509             i = this.tabs.length;
22510         }
22511         
22512         this.showPanel(this.tabs[i-1]);
22513     },
22514     
22515     
22516     addBullet: function()
22517     {
22518         if(!this.bullets || Roo.isTouch){
22519             return;
22520         }
22521         var ctr = this.el.select('.carousel-bullets',true).first();
22522         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22523         var bullet = ctr.createChild({
22524             cls : 'bullet bullet-' + i
22525         },ctr.dom.lastChild);
22526         
22527         
22528         var _this = this;
22529         
22530         bullet.on('click', (function(e, el, o, ii, t){
22531
22532             e.preventDefault();
22533
22534             this.showPanel(ii);
22535
22536             if(this.autoslide && this.slideFn){
22537                 clearInterval(this.slideFn);
22538                 this.slideFn = window.setInterval(function() {
22539                     _this.showPanelNext();
22540                 }, this.timer);
22541             }
22542
22543         }).createDelegate(this, [i, bullet], true));
22544                 
22545         
22546     },
22547      
22548     setActiveBullet : function(i)
22549     {
22550         if(Roo.isTouch){
22551             return;
22552         }
22553         
22554         Roo.each(this.el.select('.bullet', true).elements, function(el){
22555             el.removeClass('selected');
22556         });
22557
22558         var bullet = this.el.select('.bullet-' + i, true).first();
22559         
22560         if(!bullet){
22561             return;
22562         }
22563         
22564         bullet.addClass('selected');
22565     }
22566     
22567     
22568   
22569 });
22570
22571  
22572
22573  
22574  
22575 Roo.apply(Roo.bootstrap.TabGroup, {
22576     
22577     groups: {},
22578      /**
22579     * register a Navigation Group
22580     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22581     */
22582     register : function(navgrp)
22583     {
22584         this.groups[navgrp.navId] = navgrp;
22585         
22586     },
22587     /**
22588     * fetch a Navigation Group based on the navigation ID
22589     * if one does not exist , it will get created.
22590     * @param {string} the navgroup to add
22591     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22592     */
22593     get: function(navId) {
22594         if (typeof(this.groups[navId]) == 'undefined') {
22595             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22596         }
22597         return this.groups[navId] ;
22598     }
22599     
22600     
22601     
22602 });
22603
22604  /*
22605  * - LGPL
22606  *
22607  * TabPanel
22608  * 
22609  */
22610
22611 /**
22612  * @class Roo.bootstrap.TabPanel
22613  * @extends Roo.bootstrap.Component
22614  * @children Roo.bootstrap.Component
22615  * Bootstrap TabPanel class
22616  * @cfg {Boolean} active panel active
22617  * @cfg {String} html panel content
22618  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22619  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22620  * @cfg {String} href click to link..
22621  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22622  * 
22623  * 
22624  * @constructor
22625  * Create a new TabPanel
22626  * @param {Object} config The config object
22627  */
22628
22629 Roo.bootstrap.TabPanel = function(config){
22630     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22631     this.addEvents({
22632         /**
22633              * @event changed
22634              * Fires when the active status changes
22635              * @param {Roo.bootstrap.TabPanel} this
22636              * @param {Boolean} state the new state
22637             
22638          */
22639         'changed': true,
22640         /**
22641              * @event beforedeactivate
22642              * Fires before a tab is de-activated - can be used to do validation on a form.
22643              * @param {Roo.bootstrap.TabPanel} this
22644              * @return {Boolean} false if there is an error
22645             
22646          */
22647         'beforedeactivate': true
22648      });
22649     
22650     this.tabId = this.tabId || Roo.id();
22651   
22652 };
22653
22654 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22655     
22656     active: false,
22657     html: false,
22658     tabId: false,
22659     navId : false,
22660     href : '',
22661     touchSlide : false,
22662     getAutoCreate : function(){
22663         
22664         
22665         var cfg = {
22666             tag: 'div',
22667             // item is needed for carousel - not sure if it has any effect otherwise
22668             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22669             html: this.html || ''
22670         };
22671         
22672         if(this.active){
22673             cfg.cls += ' active';
22674         }
22675         
22676         if(this.tabId){
22677             cfg.tabId = this.tabId;
22678         }
22679         
22680         
22681         
22682         return cfg;
22683     },
22684     
22685     initEvents:  function()
22686     {
22687         var p = this.parent();
22688         
22689         this.navId = this.navId || p.navId;
22690         
22691         if (typeof(this.navId) != 'undefined') {
22692             // not really needed.. but just in case.. parent should be a NavGroup.
22693             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22694             
22695             tg.register(this);
22696             
22697             var i = tg.tabs.length - 1;
22698             
22699             if(this.active && tg.bullets > 0 && i < tg.bullets){
22700                 tg.setActiveBullet(i);
22701             }
22702         }
22703         
22704         this.el.on('click', this.onClick, this);
22705         
22706         if(Roo.isTouch && this.touchSlide){
22707             this.el.on("touchstart", this.onTouchStart, this);
22708             this.el.on("touchmove", this.onTouchMove, this);
22709             this.el.on("touchend", this.onTouchEnd, this);
22710         }
22711         
22712     },
22713     
22714     onRender : function(ct, position)
22715     {
22716         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22717     },
22718     
22719     setActive : function(state)
22720     {
22721         Roo.log("panel - set active " + this.tabId + "=" + state);
22722         
22723         this.active = state;
22724         if (!state) {
22725             this.el.removeClass('active');
22726             
22727         } else  if (!this.el.hasClass('active')) {
22728             this.el.addClass('active');
22729         }
22730         
22731         this.fireEvent('changed', this, state);
22732     },
22733     
22734     onClick : function(e)
22735     {
22736         e.preventDefault();
22737         
22738         if(!this.href.length){
22739             return;
22740         }
22741         
22742         window.location.href = this.href;
22743     },
22744     
22745     startX : 0,
22746     startY : 0,
22747     endX : 0,
22748     endY : 0,
22749     swiping : false,
22750     
22751     onTouchStart : function(e)
22752     {
22753         this.swiping = false;
22754         
22755         this.startX = e.browserEvent.touches[0].clientX;
22756         this.startY = e.browserEvent.touches[0].clientY;
22757     },
22758     
22759     onTouchMove : function(e)
22760     {
22761         this.swiping = true;
22762         
22763         this.endX = e.browserEvent.touches[0].clientX;
22764         this.endY = e.browserEvent.touches[0].clientY;
22765     },
22766     
22767     onTouchEnd : function(e)
22768     {
22769         if(!this.swiping){
22770             this.onClick(e);
22771             return;
22772         }
22773         
22774         var tabGroup = this.parent();
22775         
22776         if(this.endX > this.startX){ // swiping right
22777             tabGroup.showPanelPrev();
22778             return;
22779         }
22780         
22781         if(this.startX > this.endX){ // swiping left
22782             tabGroup.showPanelNext();
22783             return;
22784         }
22785     }
22786     
22787     
22788 });
22789  
22790
22791  
22792
22793  /*
22794  * - LGPL
22795  *
22796  * DateField
22797  * 
22798  */
22799
22800 /**
22801  * @class Roo.bootstrap.form.DateField
22802  * @extends Roo.bootstrap.form.Input
22803  * Bootstrap DateField class
22804  * @cfg {Number} weekStart default 0
22805  * @cfg {String} viewMode default empty, (months|years)
22806  * @cfg {String} minViewMode default empty, (months|years)
22807  * @cfg {Number} startDate default -Infinity
22808  * @cfg {Number} endDate default Infinity
22809  * @cfg {Boolean} todayHighlight default false
22810  * @cfg {Boolean} todayBtn default false
22811  * @cfg {Boolean} calendarWeeks default false
22812  * @cfg {Object} daysOfWeekDisabled default empty
22813  * @cfg {Boolean} singleMode default false (true | false)
22814  * 
22815  * @cfg {Boolean} keyboardNavigation default true
22816  * @cfg {String} language default en
22817  * 
22818  * @constructor
22819  * Create a new DateField
22820  * @param {Object} config The config object
22821  */
22822
22823 Roo.bootstrap.form.DateField = function(config){
22824     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22825      this.addEvents({
22826             /**
22827              * @event show
22828              * Fires when this field show.
22829              * @param {Roo.bootstrap.form.DateField} this
22830              * @param {Mixed} date The date value
22831              */
22832             show : true,
22833             /**
22834              * @event show
22835              * Fires when this field hide.
22836              * @param {Roo.bootstrap.form.DateField} this
22837              * @param {Mixed} date The date value
22838              */
22839             hide : true,
22840             /**
22841              * @event select
22842              * Fires when select a date.
22843              * @param {Roo.bootstrap.form.DateField} this
22844              * @param {Mixed} date The date value
22845              */
22846             select : true,
22847             /**
22848              * @event beforeselect
22849              * Fires when before select a date.
22850              * @param {Roo.bootstrap.form.DateField} this
22851              * @param {Mixed} date The date value
22852              */
22853             beforeselect : true
22854         });
22855 };
22856
22857 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22858     
22859     /**
22860      * @cfg {String} format
22861      * The default date format string which can be overriden for localization support.  The format must be
22862      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22863      */
22864     format : "m/d/y",
22865     /**
22866      * @cfg {String} altFormats
22867      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22868      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22869      */
22870     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22871     
22872     weekStart : 0,
22873     
22874     viewMode : '',
22875     
22876     minViewMode : '',
22877     
22878     todayHighlight : false,
22879     
22880     todayBtn: false,
22881     
22882     language: 'en',
22883     
22884     keyboardNavigation: true,
22885     
22886     calendarWeeks: false,
22887     
22888     startDate: -Infinity,
22889     
22890     endDate: Infinity,
22891     
22892     daysOfWeekDisabled: [],
22893     
22894     _events: [],
22895     
22896     singleMode : false,
22897     
22898     UTCDate: function()
22899     {
22900         return new Date(Date.UTC.apply(Date, arguments));
22901     },
22902     
22903     UTCToday: function()
22904     {
22905         var today = new Date();
22906         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22907     },
22908     
22909     getDate: function() {
22910             var d = this.getUTCDate();
22911             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22912     },
22913     
22914     getUTCDate: function() {
22915             return this.date;
22916     },
22917     
22918     setDate: function(d) {
22919             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22920     },
22921     
22922     setUTCDate: function(d) {
22923             this.date = d;
22924             this.setValue(this.formatDate(this.date));
22925     },
22926         
22927     onRender: function(ct, position)
22928     {
22929         
22930         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22931         
22932         this.language = this.language || 'en';
22933         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22934         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22935         
22936         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22937         this.format = this.format || 'm/d/y';
22938         this.isInline = false;
22939         this.isInput = true;
22940         this.component = this.el.select('.add-on', true).first() || false;
22941         this.component = (this.component && this.component.length === 0) ? false : this.component;
22942         this.hasInput = this.component && this.inputEl().length;
22943         
22944         if (typeof(this.minViewMode === 'string')) {
22945             switch (this.minViewMode) {
22946                 case 'months':
22947                     this.minViewMode = 1;
22948                     break;
22949                 case 'years':
22950                     this.minViewMode = 2;
22951                     break;
22952                 default:
22953                     this.minViewMode = 0;
22954                     break;
22955             }
22956         }
22957         
22958         if (typeof(this.viewMode === 'string')) {
22959             switch (this.viewMode) {
22960                 case 'months':
22961                     this.viewMode = 1;
22962                     break;
22963                 case 'years':
22964                     this.viewMode = 2;
22965                     break;
22966                 default:
22967                     this.viewMode = 0;
22968                     break;
22969             }
22970         }
22971                 
22972         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22973         
22974 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22975         
22976         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22977         
22978         this.picker().on('mousedown', this.onMousedown, this);
22979         this.picker().on('click', this.onClick, this);
22980         
22981         this.picker().addClass('datepicker-dropdown');
22982         
22983         this.startViewMode = this.viewMode;
22984         
22985         if(this.singleMode){
22986             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22987                 v.setVisibilityMode(Roo.Element.DISPLAY);
22988                 v.hide();
22989             });
22990             
22991             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22992                 v.setStyle('width', '189px');
22993             });
22994         }
22995         
22996         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22997             if(!this.calendarWeeks){
22998                 v.remove();
22999                 return;
23000             }
23001             
23002             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23003             v.attr('colspan', function(i, val){
23004                 return parseInt(val) + 1;
23005             });
23006         });
23007                         
23008         
23009         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23010         
23011         this.setStartDate(this.startDate);
23012         this.setEndDate(this.endDate);
23013         
23014         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23015         
23016         this.fillDow();
23017         this.fillMonths();
23018         this.update();
23019         this.showMode();
23020         
23021         if(this.isInline) {
23022             this.showPopup();
23023         }
23024     },
23025     
23026     picker : function()
23027     {
23028         return this.pickerEl;
23029 //        return this.el.select('.datepicker', true).first();
23030     },
23031     
23032     fillDow: function()
23033     {
23034         var dowCnt = this.weekStart;
23035         
23036         var dow = {
23037             tag: 'tr',
23038             cn: [
23039                 
23040             ]
23041         };
23042         
23043         if(this.calendarWeeks){
23044             dow.cn.push({
23045                 tag: 'th',
23046                 cls: 'cw',
23047                 html: '&nbsp;'
23048             })
23049         }
23050         
23051         while (dowCnt < this.weekStart + 7) {
23052             dow.cn.push({
23053                 tag: 'th',
23054                 cls: 'dow',
23055                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23056             });
23057         }
23058         
23059         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23060     },
23061     
23062     fillMonths: function()
23063     {    
23064         var i = 0;
23065         var months = this.picker().select('>.datepicker-months td', true).first();
23066         
23067         months.dom.innerHTML = '';
23068         
23069         while (i < 12) {
23070             var month = {
23071                 tag: 'span',
23072                 cls: 'month',
23073                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23074             };
23075             
23076             months.createChild(month);
23077         }
23078         
23079     },
23080     
23081     update: function()
23082     {
23083         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;
23084         
23085         if (this.date < this.startDate) {
23086             this.viewDate = new Date(this.startDate);
23087         } else if (this.date > this.endDate) {
23088             this.viewDate = new Date(this.endDate);
23089         } else {
23090             this.viewDate = new Date(this.date);
23091         }
23092         
23093         this.fill();
23094     },
23095     
23096     fill: function() 
23097     {
23098         var d = new Date(this.viewDate),
23099                 year = d.getUTCFullYear(),
23100                 month = d.getUTCMonth(),
23101                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23102                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23103                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23104                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23105                 currentDate = this.date && this.date.valueOf(),
23106                 today = this.UTCToday();
23107         
23108         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23109         
23110 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23111         
23112 //        this.picker.select('>tfoot th.today').
23113 //                                              .text(dates[this.language].today)
23114 //                                              .toggle(this.todayBtn !== false);
23115     
23116         this.updateNavArrows();
23117         this.fillMonths();
23118                                                 
23119         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23120         
23121         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23122          
23123         prevMonth.setUTCDate(day);
23124         
23125         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23126         
23127         var nextMonth = new Date(prevMonth);
23128         
23129         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23130         
23131         nextMonth = nextMonth.valueOf();
23132         
23133         var fillMonths = false;
23134         
23135         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23136         
23137         while(prevMonth.valueOf() <= nextMonth) {
23138             var clsName = '';
23139             
23140             if (prevMonth.getUTCDay() === this.weekStart) {
23141                 if(fillMonths){
23142                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23143                 }
23144                     
23145                 fillMonths = {
23146                     tag: 'tr',
23147                     cn: []
23148                 };
23149                 
23150                 if(this.calendarWeeks){
23151                     // ISO 8601: First week contains first thursday.
23152                     // ISO also states week starts on Monday, but we can be more abstract here.
23153                     var
23154                     // Start of current week: based on weekstart/current date
23155                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23156                     // Thursday of this week
23157                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23158                     // First Thursday of year, year from thursday
23159                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23160                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23161                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23162                     
23163                     fillMonths.cn.push({
23164                         tag: 'td',
23165                         cls: 'cw',
23166                         html: calWeek
23167                     });
23168                 }
23169             }
23170             
23171             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23172                 clsName += ' old';
23173             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23174                 clsName += ' new';
23175             }
23176             if (this.todayHighlight &&
23177                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23178                 prevMonth.getUTCMonth() == today.getMonth() &&
23179                 prevMonth.getUTCDate() == today.getDate()) {
23180                 clsName += ' today';
23181             }
23182             
23183             if (currentDate && prevMonth.valueOf() === currentDate) {
23184                 clsName += ' active';
23185             }
23186             
23187             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23188                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23189                     clsName += ' disabled';
23190             }
23191             
23192             fillMonths.cn.push({
23193                 tag: 'td',
23194                 cls: 'day ' + clsName,
23195                 html: prevMonth.getDate()
23196             });
23197             
23198             prevMonth.setDate(prevMonth.getDate()+1);
23199         }
23200           
23201         var currentYear = this.date && this.date.getUTCFullYear();
23202         var currentMonth = this.date && this.date.getUTCMonth();
23203         
23204         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23205         
23206         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23207             v.removeClass('active');
23208             
23209             if(currentYear === year && k === currentMonth){
23210                 v.addClass('active');
23211             }
23212             
23213             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23214                 v.addClass('disabled');
23215             }
23216             
23217         });
23218         
23219         
23220         year = parseInt(year/10, 10) * 10;
23221         
23222         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23223         
23224         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23225         
23226         year -= 1;
23227         for (var i = -1; i < 11; i++) {
23228             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23229                 tag: 'span',
23230                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23231                 html: year
23232             });
23233             
23234             year += 1;
23235         }
23236     },
23237     
23238     showMode: function(dir) 
23239     {
23240         if (dir) {
23241             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23242         }
23243         
23244         Roo.each(this.picker().select('>div',true).elements, function(v){
23245             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23246             v.hide();
23247         });
23248         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23249     },
23250     
23251     place: function()
23252     {
23253         if(this.isInline) {
23254             return;
23255         }
23256         
23257         this.picker().removeClass(['bottom', 'top']);
23258         
23259         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23260             /*
23261              * place to the top of element!
23262              *
23263              */
23264             
23265             this.picker().addClass('top');
23266             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23267             
23268             return;
23269         }
23270         
23271         this.picker().addClass('bottom');
23272         
23273         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23274     },
23275     
23276     parseDate : function(value)
23277     {
23278         if(!value || value instanceof Date){
23279             return value;
23280         }
23281         var v = Date.parseDate(value, this.format);
23282         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23283             v = Date.parseDate(value, 'Y-m-d');
23284         }
23285         if(!v && this.altFormats){
23286             if(!this.altFormatsArray){
23287                 this.altFormatsArray = this.altFormats.split("|");
23288             }
23289             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23290                 v = Date.parseDate(value, this.altFormatsArray[i]);
23291             }
23292         }
23293         return v;
23294     },
23295     
23296     formatDate : function(date, fmt)
23297     {   
23298         return (!date || !(date instanceof Date)) ?
23299         date : date.dateFormat(fmt || this.format);
23300     },
23301     
23302     onFocus : function()
23303     {
23304         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23305         this.showPopup();
23306     },
23307     
23308     onBlur : function()
23309     {
23310         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23311         
23312         var d = this.inputEl().getValue();
23313         
23314         this.setValue(d);
23315                 
23316         this.hidePopup();
23317     },
23318     
23319     showPopup : function()
23320     {
23321         this.picker().show();
23322         this.update();
23323         this.place();
23324         
23325         this.fireEvent('showpopup', this, this.date);
23326     },
23327     
23328     hidePopup : function()
23329     {
23330         if(this.isInline) {
23331             return;
23332         }
23333         this.picker().hide();
23334         this.viewMode = this.startViewMode;
23335         this.showMode();
23336         
23337         this.fireEvent('hidepopup', this, this.date);
23338         
23339     },
23340     
23341     onMousedown: function(e)
23342     {
23343         e.stopPropagation();
23344         e.preventDefault();
23345     },
23346     
23347     keyup: function(e)
23348     {
23349         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23350         this.update();
23351     },
23352
23353     setValue: function(v)
23354     {
23355         if(this.fireEvent('beforeselect', this, v) !== false){
23356             var d = new Date(this.parseDate(v) ).clearTime();
23357         
23358             if(isNaN(d.getTime())){
23359                 this.date = this.viewDate = '';
23360                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23361                 return;
23362             }
23363
23364             v = this.formatDate(d);
23365
23366             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23367
23368             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23369
23370             this.update();
23371
23372             this.fireEvent('select', this, this.date);
23373         }
23374     },
23375     
23376     getValue: function()
23377     {
23378         return this.formatDate(this.date);
23379     },
23380     
23381     fireKey: function(e)
23382     {
23383         if (!this.picker().isVisible()){
23384             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23385                 this.showPopup();
23386             }
23387             return;
23388         }
23389         
23390         var dateChanged = false,
23391         dir, day, month,
23392         newDate, newViewDate;
23393         
23394         switch(e.keyCode){
23395             case 27: // escape
23396                 this.hidePopup();
23397                 e.preventDefault();
23398                 break;
23399             case 37: // left
23400             case 39: // right
23401                 if (!this.keyboardNavigation) {
23402                     break;
23403                 }
23404                 dir = e.keyCode == 37 ? -1 : 1;
23405                 
23406                 if (e.ctrlKey){
23407                     newDate = this.moveYear(this.date, dir);
23408                     newViewDate = this.moveYear(this.viewDate, dir);
23409                 } else if (e.shiftKey){
23410                     newDate = this.moveMonth(this.date, dir);
23411                     newViewDate = this.moveMonth(this.viewDate, dir);
23412                 } else {
23413                     newDate = new Date(this.date);
23414                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23415                     newViewDate = new Date(this.viewDate);
23416                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23417                 }
23418                 if (this.dateWithinRange(newDate)){
23419                     this.date = newDate;
23420                     this.viewDate = newViewDate;
23421                     this.setValue(this.formatDate(this.date));
23422 //                    this.update();
23423                     e.preventDefault();
23424                     dateChanged = true;
23425                 }
23426                 break;
23427             case 38: // up
23428             case 40: // down
23429                 if (!this.keyboardNavigation) {
23430                     break;
23431                 }
23432                 dir = e.keyCode == 38 ? -1 : 1;
23433                 if (e.ctrlKey){
23434                     newDate = this.moveYear(this.date, dir);
23435                     newViewDate = this.moveYear(this.viewDate, dir);
23436                 } else if (e.shiftKey){
23437                     newDate = this.moveMonth(this.date, dir);
23438                     newViewDate = this.moveMonth(this.viewDate, dir);
23439                 } else {
23440                     newDate = new Date(this.date);
23441                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23442                     newViewDate = new Date(this.viewDate);
23443                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23444                 }
23445                 if (this.dateWithinRange(newDate)){
23446                     this.date = newDate;
23447                     this.viewDate = newViewDate;
23448                     this.setValue(this.formatDate(this.date));
23449 //                    this.update();
23450                     e.preventDefault();
23451                     dateChanged = true;
23452                 }
23453                 break;
23454             case 13: // enter
23455                 this.setValue(this.formatDate(this.date));
23456                 this.hidePopup();
23457                 e.preventDefault();
23458                 break;
23459             case 9: // tab
23460                 this.setValue(this.formatDate(this.date));
23461                 this.hidePopup();
23462                 break;
23463             case 16: // shift
23464             case 17: // ctrl
23465             case 18: // alt
23466                 break;
23467             default :
23468                 this.hidePopup();
23469                 
23470         }
23471     },
23472     
23473     
23474     onClick: function(e) 
23475     {
23476         e.stopPropagation();
23477         e.preventDefault();
23478         
23479         var target = e.getTarget();
23480         
23481         if(target.nodeName.toLowerCase() === 'i'){
23482             target = Roo.get(target).dom.parentNode;
23483         }
23484         
23485         var nodeName = target.nodeName;
23486         var className = target.className;
23487         var html = target.innerHTML;
23488         //Roo.log(nodeName);
23489         
23490         switch(nodeName.toLowerCase()) {
23491             case 'th':
23492                 switch(className) {
23493                     case 'switch':
23494                         this.showMode(1);
23495                         break;
23496                     case 'prev':
23497                     case 'next':
23498                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23499                         switch(this.viewMode){
23500                                 case 0:
23501                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23502                                         break;
23503                                 case 1:
23504                                 case 2:
23505                                         this.viewDate = this.moveYear(this.viewDate, dir);
23506                                         break;
23507                         }
23508                         this.fill();
23509                         break;
23510                     case 'today':
23511                         var date = new Date();
23512                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23513 //                        this.fill()
23514                         this.setValue(this.formatDate(this.date));
23515                         
23516                         this.hidePopup();
23517                         break;
23518                 }
23519                 break;
23520             case 'span':
23521                 if (className.indexOf('disabled') < 0) {
23522                 if (!this.viewDate) {
23523                     this.viewDate = new Date();
23524                 }
23525                 this.viewDate.setUTCDate(1);
23526                     if (className.indexOf('month') > -1) {
23527                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23528                     } else {
23529                         var year = parseInt(html, 10) || 0;
23530                         this.viewDate.setUTCFullYear(year);
23531                         
23532                     }
23533                     
23534                     if(this.singleMode){
23535                         this.setValue(this.formatDate(this.viewDate));
23536                         this.hidePopup();
23537                         return;
23538                     }
23539                     
23540                     this.showMode(-1);
23541                     this.fill();
23542                 }
23543                 break;
23544                 
23545             case 'td':
23546                 //Roo.log(className);
23547                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23548                     var day = parseInt(html, 10) || 1;
23549                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23550                         month = (this.viewDate || new Date()).getUTCMonth();
23551
23552                     if (className.indexOf('old') > -1) {
23553                         if(month === 0 ){
23554                             month = 11;
23555                             year -= 1;
23556                         }else{
23557                             month -= 1;
23558                         }
23559                     } else if (className.indexOf('new') > -1) {
23560                         if (month == 11) {
23561                             month = 0;
23562                             year += 1;
23563                         } else {
23564                             month += 1;
23565                         }
23566                     }
23567                     //Roo.log([year,month,day]);
23568                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23569                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23570 //                    this.fill();
23571                     //Roo.log(this.formatDate(this.date));
23572                     this.setValue(this.formatDate(this.date));
23573                     this.hidePopup();
23574                 }
23575                 break;
23576         }
23577     },
23578     
23579     setStartDate: function(startDate)
23580     {
23581         this.startDate = startDate || -Infinity;
23582         if (this.startDate !== -Infinity) {
23583             this.startDate = this.parseDate(this.startDate);
23584         }
23585         this.update();
23586         this.updateNavArrows();
23587     },
23588
23589     setEndDate: function(endDate)
23590     {
23591         this.endDate = endDate || Infinity;
23592         if (this.endDate !== Infinity) {
23593             this.endDate = this.parseDate(this.endDate);
23594         }
23595         this.update();
23596         this.updateNavArrows();
23597     },
23598     
23599     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23600     {
23601         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23602         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23603             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23604         }
23605         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23606             return parseInt(d, 10);
23607         });
23608         this.update();
23609         this.updateNavArrows();
23610     },
23611     
23612     updateNavArrows: function() 
23613     {
23614         if(this.singleMode){
23615             return;
23616         }
23617         
23618         var d = new Date(this.viewDate),
23619         year = d.getUTCFullYear(),
23620         month = d.getUTCMonth();
23621         
23622         Roo.each(this.picker().select('.prev', true).elements, function(v){
23623             v.show();
23624             switch (this.viewMode) {
23625                 case 0:
23626
23627                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23628                         v.hide();
23629                     }
23630                     break;
23631                 case 1:
23632                 case 2:
23633                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23634                         v.hide();
23635                     }
23636                     break;
23637             }
23638         });
23639         
23640         Roo.each(this.picker().select('.next', true).elements, function(v){
23641             v.show();
23642             switch (this.viewMode) {
23643                 case 0:
23644
23645                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23646                         v.hide();
23647                     }
23648                     break;
23649                 case 1:
23650                 case 2:
23651                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23652                         v.hide();
23653                     }
23654                     break;
23655             }
23656         })
23657     },
23658     
23659     moveMonth: function(date, dir)
23660     {
23661         if (!dir) {
23662             return date;
23663         }
23664         var new_date = new Date(date.valueOf()),
23665         day = new_date.getUTCDate(),
23666         month = new_date.getUTCMonth(),
23667         mag = Math.abs(dir),
23668         new_month, test;
23669         dir = dir > 0 ? 1 : -1;
23670         if (mag == 1){
23671             test = dir == -1
23672             // If going back one month, make sure month is not current month
23673             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23674             ? function(){
23675                 return new_date.getUTCMonth() == month;
23676             }
23677             // If going forward one month, make sure month is as expected
23678             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23679             : function(){
23680                 return new_date.getUTCMonth() != new_month;
23681             };
23682             new_month = month + dir;
23683             new_date.setUTCMonth(new_month);
23684             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23685             if (new_month < 0 || new_month > 11) {
23686                 new_month = (new_month + 12) % 12;
23687             }
23688         } else {
23689             // For magnitudes >1, move one month at a time...
23690             for (var i=0; i<mag; i++) {
23691                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23692                 new_date = this.moveMonth(new_date, dir);
23693             }
23694             // ...then reset the day, keeping it in the new month
23695             new_month = new_date.getUTCMonth();
23696             new_date.setUTCDate(day);
23697             test = function(){
23698                 return new_month != new_date.getUTCMonth();
23699             };
23700         }
23701         // Common date-resetting loop -- if date is beyond end of month, make it
23702         // end of month
23703         while (test()){
23704             new_date.setUTCDate(--day);
23705             new_date.setUTCMonth(new_month);
23706         }
23707         return new_date;
23708     },
23709
23710     moveYear: function(date, dir)
23711     {
23712         return this.moveMonth(date, dir*12);
23713     },
23714
23715     dateWithinRange: function(date)
23716     {
23717         return date >= this.startDate && date <= this.endDate;
23718     },
23719
23720     
23721     remove: function() 
23722     {
23723         this.picker().remove();
23724     },
23725     
23726     validateValue : function(value)
23727     {
23728         if(this.getVisibilityEl().hasClass('hidden')){
23729             return true;
23730         }
23731         
23732         if(value.length < 1)  {
23733             if(this.allowBlank){
23734                 return true;
23735             }
23736             return false;
23737         }
23738         
23739         if(value.length < this.minLength){
23740             return false;
23741         }
23742         if(value.length > this.maxLength){
23743             return false;
23744         }
23745         if(this.vtype){
23746             var vt = Roo.form.VTypes;
23747             if(!vt[this.vtype](value, this)){
23748                 return false;
23749             }
23750         }
23751         if(typeof this.validator == "function"){
23752             var msg = this.validator(value);
23753             if(msg !== true){
23754                 return false;
23755             }
23756         }
23757         
23758         if(this.regex && !this.regex.test(value)){
23759             return false;
23760         }
23761         
23762         if(typeof(this.parseDate(value)) == 'undefined'){
23763             return false;
23764         }
23765         
23766         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23767             return false;
23768         }      
23769         
23770         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23771             return false;
23772         } 
23773         
23774         
23775         return true;
23776     },
23777     
23778     reset : function()
23779     {
23780         this.date = this.viewDate = '';
23781         
23782         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23783     }
23784    
23785 });
23786
23787 Roo.apply(Roo.bootstrap.form.DateField,  {
23788     
23789     head : {
23790         tag: 'thead',
23791         cn: [
23792         {
23793             tag: 'tr',
23794             cn: [
23795             {
23796                 tag: 'th',
23797                 cls: 'prev',
23798                 html: '<i class="fa fa-arrow-left"/>'
23799             },
23800             {
23801                 tag: 'th',
23802                 cls: 'switch',
23803                 colspan: '5'
23804             },
23805             {
23806                 tag: 'th',
23807                 cls: 'next',
23808                 html: '<i class="fa fa-arrow-right"/>'
23809             }
23810
23811             ]
23812         }
23813         ]
23814     },
23815     
23816     content : {
23817         tag: 'tbody',
23818         cn: [
23819         {
23820             tag: 'tr',
23821             cn: [
23822             {
23823                 tag: 'td',
23824                 colspan: '7'
23825             }
23826             ]
23827         }
23828         ]
23829     },
23830     
23831     footer : {
23832         tag: 'tfoot',
23833         cn: [
23834         {
23835             tag: 'tr',
23836             cn: [
23837             {
23838                 tag: 'th',
23839                 colspan: '7',
23840                 cls: 'today'
23841             }
23842                     
23843             ]
23844         }
23845         ]
23846     },
23847     
23848     dates:{
23849         en: {
23850             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23851             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23852             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23853             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23854             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23855             today: "Today"
23856         }
23857     },
23858     
23859     modes: [
23860     {
23861         clsName: 'days',
23862         navFnc: 'Month',
23863         navStep: 1
23864     },
23865     {
23866         clsName: 'months',
23867         navFnc: 'FullYear',
23868         navStep: 1
23869     },
23870     {
23871         clsName: 'years',
23872         navFnc: 'FullYear',
23873         navStep: 10
23874     }]
23875 });
23876
23877 Roo.apply(Roo.bootstrap.form.DateField,  {
23878   
23879     template : {
23880         tag: 'div',
23881         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23882         cn: [
23883         {
23884             tag: 'div',
23885             cls: 'datepicker-days',
23886             cn: [
23887             {
23888                 tag: 'table',
23889                 cls: 'table-condensed',
23890                 cn:[
23891                 Roo.bootstrap.form.DateField.head,
23892                 {
23893                     tag: 'tbody'
23894                 },
23895                 Roo.bootstrap.form.DateField.footer
23896                 ]
23897             }
23898             ]
23899         },
23900         {
23901             tag: 'div',
23902             cls: 'datepicker-months',
23903             cn: [
23904             {
23905                 tag: 'table',
23906                 cls: 'table-condensed',
23907                 cn:[
23908                 Roo.bootstrap.form.DateField.head,
23909                 Roo.bootstrap.form.DateField.content,
23910                 Roo.bootstrap.form.DateField.footer
23911                 ]
23912             }
23913             ]
23914         },
23915         {
23916             tag: 'div',
23917             cls: 'datepicker-years',
23918             cn: [
23919             {
23920                 tag: 'table',
23921                 cls: 'table-condensed',
23922                 cn:[
23923                 Roo.bootstrap.form.DateField.head,
23924                 Roo.bootstrap.form.DateField.content,
23925                 Roo.bootstrap.form.DateField.footer
23926                 ]
23927             }
23928             ]
23929         }
23930         ]
23931     }
23932 });
23933
23934  
23935
23936  /*
23937  * - LGPL
23938  *
23939  * TimeField
23940  * 
23941  */
23942
23943 /**
23944  * @class Roo.bootstrap.form.TimeField
23945  * @extends Roo.bootstrap.form.Input
23946  * Bootstrap DateField class
23947  * 
23948  * 
23949  * @constructor
23950  * Create a new TimeField
23951  * @param {Object} config The config object
23952  */
23953
23954 Roo.bootstrap.form.TimeField = function(config){
23955     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23956     this.addEvents({
23957             /**
23958              * @event show
23959              * Fires when this field show.
23960              * @param {Roo.bootstrap.form.DateField} thisthis
23961              * @param {Mixed} date The date value
23962              */
23963             show : true,
23964             /**
23965              * @event show
23966              * Fires when this field hide.
23967              * @param {Roo.bootstrap.form.DateField} this
23968              * @param {Mixed} date The date value
23969              */
23970             hide : true,
23971             /**
23972              * @event select
23973              * Fires when select a date.
23974              * @param {Roo.bootstrap.form.DateField} this
23975              * @param {Mixed} date The date value
23976              */
23977             select : true
23978         });
23979 };
23980
23981 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23982     
23983     /**
23984      * @cfg {String} format
23985      * The default time format string which can be overriden for localization support.  The format must be
23986      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23987      */
23988     format : "H:i",
23989
23990     getAutoCreate : function()
23991     {
23992         this.after = '<i class="fa far fa-clock"></i>';
23993         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23994         
23995          
23996     },
23997     onRender: function(ct, position)
23998     {
23999         
24000         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24001                 
24002         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24003         
24004         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24005         
24006         this.pop = this.picker().select('>.datepicker-time',true).first();
24007         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24008         
24009         this.picker().on('mousedown', this.onMousedown, this);
24010         this.picker().on('click', this.onClick, this);
24011         
24012         this.picker().addClass('datepicker-dropdown');
24013     
24014         this.fillTime();
24015         this.update();
24016             
24017         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24018         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24019         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24020         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24021         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24022         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24023
24024     },
24025     
24026     fireKey: function(e){
24027         if (!this.picker().isVisible()){
24028             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24029                 this.show();
24030             }
24031             return;
24032         }
24033
24034         e.preventDefault();
24035         
24036         switch(e.keyCode){
24037             case 27: // escape
24038                 this.hide();
24039                 break;
24040             case 37: // left
24041             case 39: // right
24042                 this.onTogglePeriod();
24043                 break;
24044             case 38: // up
24045                 this.onIncrementMinutes();
24046                 break;
24047             case 40: // down
24048                 this.onDecrementMinutes();
24049                 break;
24050             case 13: // enter
24051             case 9: // tab
24052                 this.setTime();
24053                 break;
24054         }
24055     },
24056     
24057     onClick: function(e) {
24058         e.stopPropagation();
24059         e.preventDefault();
24060     },
24061     
24062     picker : function()
24063     {
24064         return this.pickerEl;
24065     },
24066     
24067     fillTime: function()
24068     {    
24069         var time = this.pop.select('tbody', true).first();
24070         
24071         time.dom.innerHTML = '';
24072         
24073         time.createChild({
24074             tag: 'tr',
24075             cn: [
24076                 {
24077                     tag: 'td',
24078                     cn: [
24079                         {
24080                             tag: 'a',
24081                             href: '#',
24082                             cls: 'btn',
24083                             cn: [
24084                                 {
24085                                     tag: 'i',
24086                                     cls: 'hours-up fa fas fa-chevron-up'
24087                                 }
24088                             ]
24089                         } 
24090                     ]
24091                 },
24092                 {
24093                     tag: 'td',
24094                     cls: 'separator'
24095                 },
24096                 {
24097                     tag: 'td',
24098                     cn: [
24099                         {
24100                             tag: 'a',
24101                             href: '#',
24102                             cls: 'btn',
24103                             cn: [
24104                                 {
24105                                     tag: 'i',
24106                                     cls: 'minutes-up fa fas fa-chevron-up'
24107                                 }
24108                             ]
24109                         }
24110                     ]
24111                 },
24112                 {
24113                     tag: 'td',
24114                     cls: 'separator'
24115                 }
24116             ]
24117         });
24118         
24119         time.createChild({
24120             tag: 'tr',
24121             cn: [
24122                 {
24123                     tag: 'td',
24124                     cn: [
24125                         {
24126                             tag: 'span',
24127                             cls: 'timepicker-hour',
24128                             html: '00'
24129                         }  
24130                     ]
24131                 },
24132                 {
24133                     tag: 'td',
24134                     cls: 'separator',
24135                     html: ':'
24136                 },
24137                 {
24138                     tag: 'td',
24139                     cn: [
24140                         {
24141                             tag: 'span',
24142                             cls: 'timepicker-minute',
24143                             html: '00'
24144                         }  
24145                     ]
24146                 },
24147                 {
24148                     tag: 'td',
24149                     cls: 'separator'
24150                 },
24151                 {
24152                     tag: 'td',
24153                     cn: [
24154                         {
24155                             tag: 'button',
24156                             type: 'button',
24157                             cls: 'btn btn-primary period',
24158                             html: 'AM'
24159                             
24160                         }
24161                     ]
24162                 }
24163             ]
24164         });
24165         
24166         time.createChild({
24167             tag: 'tr',
24168             cn: [
24169                 {
24170                     tag: 'td',
24171                     cn: [
24172                         {
24173                             tag: 'a',
24174                             href: '#',
24175                             cls: 'btn',
24176                             cn: [
24177                                 {
24178                                     tag: 'span',
24179                                     cls: 'hours-down fa fas fa-chevron-down'
24180                                 }
24181                             ]
24182                         }
24183                     ]
24184                 },
24185                 {
24186                     tag: 'td',
24187                     cls: 'separator'
24188                 },
24189                 {
24190                     tag: 'td',
24191                     cn: [
24192                         {
24193                             tag: 'a',
24194                             href: '#',
24195                             cls: 'btn',
24196                             cn: [
24197                                 {
24198                                     tag: 'span',
24199                                     cls: 'minutes-down fa fas fa-chevron-down'
24200                                 }
24201                             ]
24202                         }
24203                     ]
24204                 },
24205                 {
24206                     tag: 'td',
24207                     cls: 'separator'
24208                 }
24209             ]
24210         });
24211         
24212     },
24213     
24214     update: function()
24215     {
24216         
24217         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24218         
24219         this.fill();
24220     },
24221     
24222     fill: function() 
24223     {
24224         var hours = this.time.getHours();
24225         var minutes = this.time.getMinutes();
24226         var period = 'AM';
24227         
24228         if(hours > 11){
24229             period = 'PM';
24230         }
24231         
24232         if(hours == 0){
24233             hours = 12;
24234         }
24235         
24236         
24237         if(hours > 12){
24238             hours = hours - 12;
24239         }
24240         
24241         if(hours < 10){
24242             hours = '0' + hours;
24243         }
24244         
24245         if(minutes < 10){
24246             minutes = '0' + minutes;
24247         }
24248         
24249         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24250         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24251         this.pop.select('button', true).first().dom.innerHTML = period;
24252         
24253     },
24254     
24255     place: function()
24256     {   
24257         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24258         
24259         var cls = ['bottom'];
24260         
24261         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24262             cls.pop();
24263             cls.push('top');
24264         }
24265         
24266         cls.push('right');
24267         
24268         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24269             cls.pop();
24270             cls.push('left');
24271         }
24272         //this.picker().setXY(20000,20000);
24273         this.picker().addClass(cls.join('-'));
24274         
24275         var _this = this;
24276         
24277         Roo.each(cls, function(c){
24278             if(c == 'bottom'){
24279                 (function() {
24280                  //  
24281                 }).defer(200);
24282                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24283                 //_this.picker().setTop(_this.inputEl().getHeight());
24284                 return;
24285             }
24286             if(c == 'top'){
24287                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24288                 
24289                 //_this.picker().setTop(0 - _this.picker().getHeight());
24290                 return;
24291             }
24292             /*
24293             if(c == 'left'){
24294                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24295                 return;
24296             }
24297             if(c == 'right'){
24298                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24299                 return;
24300             }
24301             */
24302         });
24303         
24304     },
24305   
24306     onFocus : function()
24307     {
24308         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24309         this.show();
24310     },
24311     
24312     onBlur : function()
24313     {
24314         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24315         this.hide();
24316     },
24317     
24318     show : function()
24319     {
24320         this.picker().show();
24321         this.pop.show();
24322         this.update();
24323         this.place();
24324         
24325         this.fireEvent('show', this, this.date);
24326     },
24327     
24328     hide : function()
24329     {
24330         this.picker().hide();
24331         this.pop.hide();
24332         
24333         this.fireEvent('hide', this, this.date);
24334     },
24335     
24336     setTime : function()
24337     {
24338         this.hide();
24339         this.setValue(this.time.format(this.format));
24340         
24341         this.fireEvent('select', this, this.date);
24342         
24343         
24344     },
24345     
24346     onMousedown: function(e){
24347         e.stopPropagation();
24348         e.preventDefault();
24349     },
24350     
24351     onIncrementHours: function()
24352     {
24353         Roo.log('onIncrementHours');
24354         this.time = this.time.add(Date.HOUR, 1);
24355         this.update();
24356         
24357     },
24358     
24359     onDecrementHours: function()
24360     {
24361         Roo.log('onDecrementHours');
24362         this.time = this.time.add(Date.HOUR, -1);
24363         this.update();
24364     },
24365     
24366     onIncrementMinutes: function()
24367     {
24368         Roo.log('onIncrementMinutes');
24369         this.time = this.time.add(Date.MINUTE, 1);
24370         this.update();
24371     },
24372     
24373     onDecrementMinutes: function()
24374     {
24375         Roo.log('onDecrementMinutes');
24376         this.time = this.time.add(Date.MINUTE, -1);
24377         this.update();
24378     },
24379     
24380     onTogglePeriod: function()
24381     {
24382         Roo.log('onTogglePeriod');
24383         this.time = this.time.add(Date.HOUR, 12);
24384         this.update();
24385     }
24386     
24387    
24388 });
24389  
24390
24391 Roo.apply(Roo.bootstrap.form.TimeField,  {
24392   
24393     template : {
24394         tag: 'div',
24395         cls: 'datepicker dropdown-menu',
24396         cn: [
24397             {
24398                 tag: 'div',
24399                 cls: 'datepicker-time',
24400                 cn: [
24401                 {
24402                     tag: 'table',
24403                     cls: 'table-condensed',
24404                     cn:[
24405                         {
24406                             tag: 'tbody',
24407                             cn: [
24408                                 {
24409                                     tag: 'tr',
24410                                     cn: [
24411                                     {
24412                                         tag: 'td',
24413                                         colspan: '7'
24414                                     }
24415                                     ]
24416                                 }
24417                             ]
24418                         },
24419                         {
24420                             tag: 'tfoot',
24421                             cn: [
24422                                 {
24423                                     tag: 'tr',
24424                                     cn: [
24425                                     {
24426                                         tag: 'th',
24427                                         colspan: '7',
24428                                         cls: '',
24429                                         cn: [
24430                                             {
24431                                                 tag: 'button',
24432                                                 cls: 'btn btn-info ok',
24433                                                 html: 'OK'
24434                                             }
24435                                         ]
24436                                     }
24437                     
24438                                     ]
24439                                 }
24440                             ]
24441                         }
24442                     ]
24443                 }
24444                 ]
24445             }
24446         ]
24447     }
24448 });
24449
24450  
24451
24452  /*
24453  * - LGPL
24454  *
24455  * MonthField
24456  * 
24457  */
24458
24459 /**
24460  * @class Roo.bootstrap.form.MonthField
24461  * @extends Roo.bootstrap.form.Input
24462  * Bootstrap MonthField class
24463  * 
24464  * @cfg {String} language default en
24465  * 
24466  * @constructor
24467  * Create a new MonthField
24468  * @param {Object} config The config object
24469  */
24470
24471 Roo.bootstrap.form.MonthField = function(config){
24472     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24473     
24474     this.addEvents({
24475         /**
24476          * @event show
24477          * Fires when this field show.
24478          * @param {Roo.bootstrap.form.MonthField} this
24479          * @param {Mixed} date The date value
24480          */
24481         show : true,
24482         /**
24483          * @event show
24484          * Fires when this field hide.
24485          * @param {Roo.bootstrap.form.MonthField} this
24486          * @param {Mixed} date The date value
24487          */
24488         hide : true,
24489         /**
24490          * @event select
24491          * Fires when select a date.
24492          * @param {Roo.bootstrap.form.MonthField} this
24493          * @param {String} oldvalue The old value
24494          * @param {String} newvalue The new value
24495          */
24496         select : true
24497     });
24498 };
24499
24500 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24501     
24502     onRender: function(ct, position)
24503     {
24504         
24505         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24506         
24507         this.language = this.language || 'en';
24508         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24509         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24510         
24511         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24512         this.isInline = false;
24513         this.isInput = true;
24514         this.component = this.el.select('.add-on', true).first() || false;
24515         this.component = (this.component && this.component.length === 0) ? false : this.component;
24516         this.hasInput = this.component && this.inputEL().length;
24517         
24518         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24519         
24520         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24521         
24522         this.picker().on('mousedown', this.onMousedown, this);
24523         this.picker().on('click', this.onClick, this);
24524         
24525         this.picker().addClass('datepicker-dropdown');
24526         
24527         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24528             v.setStyle('width', '189px');
24529         });
24530         
24531         this.fillMonths();
24532         
24533         this.update();
24534         
24535         if(this.isInline) {
24536             this.show();
24537         }
24538         
24539     },
24540     
24541     setValue: function(v, suppressEvent)
24542     {   
24543         var o = this.getValue();
24544         
24545         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24546         
24547         this.update();
24548
24549         if(suppressEvent !== true){
24550             this.fireEvent('select', this, o, v);
24551         }
24552         
24553     },
24554     
24555     getValue: function()
24556     {
24557         return this.value;
24558     },
24559     
24560     onClick: function(e) 
24561     {
24562         e.stopPropagation();
24563         e.preventDefault();
24564         
24565         var target = e.getTarget();
24566         
24567         if(target.nodeName.toLowerCase() === 'i'){
24568             target = Roo.get(target).dom.parentNode;
24569         }
24570         
24571         var nodeName = target.nodeName;
24572         var className = target.className;
24573         var html = target.innerHTML;
24574         
24575         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24576             return;
24577         }
24578         
24579         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24580         
24581         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24582         
24583         this.hide();
24584                         
24585     },
24586     
24587     picker : function()
24588     {
24589         return this.pickerEl;
24590     },
24591     
24592     fillMonths: function()
24593     {    
24594         var i = 0;
24595         var months = this.picker().select('>.datepicker-months td', true).first();
24596         
24597         months.dom.innerHTML = '';
24598         
24599         while (i < 12) {
24600             var month = {
24601                 tag: 'span',
24602                 cls: 'month',
24603                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24604             };
24605             
24606             months.createChild(month);
24607         }
24608         
24609     },
24610     
24611     update: function()
24612     {
24613         var _this = this;
24614         
24615         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24616             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24617         }
24618         
24619         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24620             e.removeClass('active');
24621             
24622             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24623                 e.addClass('active');
24624             }
24625         })
24626     },
24627     
24628     place: function()
24629     {
24630         if(this.isInline) {
24631             return;
24632         }
24633         
24634         this.picker().removeClass(['bottom', 'top']);
24635         
24636         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24637             /*
24638              * place to the top of element!
24639              *
24640              */
24641             
24642             this.picker().addClass('top');
24643             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24644             
24645             return;
24646         }
24647         
24648         this.picker().addClass('bottom');
24649         
24650         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24651     },
24652     
24653     onFocus : function()
24654     {
24655         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24656         this.show();
24657     },
24658     
24659     onBlur : function()
24660     {
24661         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24662         
24663         var d = this.inputEl().getValue();
24664         
24665         this.setValue(d);
24666                 
24667         this.hide();
24668     },
24669     
24670     show : function()
24671     {
24672         this.picker().show();
24673         this.picker().select('>.datepicker-months', true).first().show();
24674         this.update();
24675         this.place();
24676         
24677         this.fireEvent('show', this, this.date);
24678     },
24679     
24680     hide : function()
24681     {
24682         if(this.isInline) {
24683             return;
24684         }
24685         this.picker().hide();
24686         this.fireEvent('hide', this, this.date);
24687         
24688     },
24689     
24690     onMousedown: function(e)
24691     {
24692         e.stopPropagation();
24693         e.preventDefault();
24694     },
24695     
24696     keyup: function(e)
24697     {
24698         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24699         this.update();
24700     },
24701
24702     fireKey: function(e)
24703     {
24704         if (!this.picker().isVisible()){
24705             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24706                 this.show();
24707             }
24708             return;
24709         }
24710         
24711         var dir;
24712         
24713         switch(e.keyCode){
24714             case 27: // escape
24715                 this.hide();
24716                 e.preventDefault();
24717                 break;
24718             case 37: // left
24719             case 39: // right
24720                 dir = e.keyCode == 37 ? -1 : 1;
24721                 
24722                 this.vIndex = this.vIndex + dir;
24723                 
24724                 if(this.vIndex < 0){
24725                     this.vIndex = 0;
24726                 }
24727                 
24728                 if(this.vIndex > 11){
24729                     this.vIndex = 11;
24730                 }
24731                 
24732                 if(isNaN(this.vIndex)){
24733                     this.vIndex = 0;
24734                 }
24735                 
24736                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24737                 
24738                 break;
24739             case 38: // up
24740             case 40: // down
24741                 
24742                 dir = e.keyCode == 38 ? -1 : 1;
24743                 
24744                 this.vIndex = this.vIndex + dir * 4;
24745                 
24746                 if(this.vIndex < 0){
24747                     this.vIndex = 0;
24748                 }
24749                 
24750                 if(this.vIndex > 11){
24751                     this.vIndex = 11;
24752                 }
24753                 
24754                 if(isNaN(this.vIndex)){
24755                     this.vIndex = 0;
24756                 }
24757                 
24758                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24759                 break;
24760                 
24761             case 13: // enter
24762                 
24763                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24764                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24765                 }
24766                 
24767                 this.hide();
24768                 e.preventDefault();
24769                 break;
24770             case 9: // tab
24771                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24772                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24773                 }
24774                 this.hide();
24775                 break;
24776             case 16: // shift
24777             case 17: // ctrl
24778             case 18: // alt
24779                 break;
24780             default :
24781                 this.hide();
24782                 
24783         }
24784     },
24785     
24786     remove: function() 
24787     {
24788         this.picker().remove();
24789     }
24790    
24791 });
24792
24793 Roo.apply(Roo.bootstrap.form.MonthField,  {
24794     
24795     content : {
24796         tag: 'tbody',
24797         cn: [
24798         {
24799             tag: 'tr',
24800             cn: [
24801             {
24802                 tag: 'td',
24803                 colspan: '7'
24804             }
24805             ]
24806         }
24807         ]
24808     },
24809     
24810     dates:{
24811         en: {
24812             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24813             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24814         }
24815     }
24816 });
24817
24818 Roo.apply(Roo.bootstrap.form.MonthField,  {
24819   
24820     template : {
24821         tag: 'div',
24822         cls: 'datepicker dropdown-menu roo-dynamic',
24823         cn: [
24824             {
24825                 tag: 'div',
24826                 cls: 'datepicker-months',
24827                 cn: [
24828                 {
24829                     tag: 'table',
24830                     cls: 'table-condensed',
24831                     cn:[
24832                         Roo.bootstrap.form.DateField.content
24833                     ]
24834                 }
24835                 ]
24836             }
24837         ]
24838     }
24839 });
24840
24841  
24842
24843  
24844  /*
24845  * - LGPL
24846  *
24847  * CheckBox
24848  * 
24849  */
24850
24851 /**
24852  * @class Roo.bootstrap.form.CheckBox
24853  * @extends Roo.bootstrap.form.Input
24854  * Bootstrap CheckBox class
24855  * 
24856  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24857  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24858  * @cfg {String} boxLabel The text that appears beside the checkbox
24859  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24860  * @cfg {Boolean} checked initnal the element
24861  * @cfg {Boolean} inline inline the element (default false)
24862  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24863  * @cfg {String} tooltip label tooltip
24864  * 
24865  * @constructor
24866  * Create a new CheckBox
24867  * @param {Object} config The config object
24868  */
24869
24870 Roo.bootstrap.form.CheckBox = function(config){
24871     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24872    
24873     this.addEvents({
24874         /**
24875         * @event check
24876         * Fires when the element is checked or unchecked.
24877         * @param {Roo.bootstrap.form.CheckBox} this This input
24878         * @param {Boolean} checked The new checked value
24879         */
24880        check : true,
24881        /**
24882         * @event click
24883         * Fires when the element is click.
24884         * @param {Roo.bootstrap.form.CheckBox} this This input
24885         */
24886        click : true
24887     });
24888     
24889 };
24890
24891 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24892   
24893     inputType: 'checkbox',
24894     inputValue: 1,
24895     valueOff: 0,
24896     boxLabel: false,
24897     checked: false,
24898     weight : false,
24899     inline: false,
24900     tooltip : '',
24901     
24902     // checkbox success does not make any sense really.. 
24903     invalidClass : "",
24904     validClass : "",
24905     
24906     
24907     getAutoCreate : function()
24908     {
24909         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24910         
24911         var id = Roo.id();
24912         
24913         var cfg = {};
24914         
24915         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24916         
24917         if(this.inline){
24918             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24919         }
24920         
24921         var input =  {
24922             tag: 'input',
24923             id : id,
24924             type : this.inputType,
24925             value : this.inputValue,
24926             cls : 'roo-' + this.inputType, //'form-box',
24927             placeholder : this.placeholder || ''
24928             
24929         };
24930         
24931         if(this.inputType != 'radio'){
24932             var hidden =  {
24933                 tag: 'input',
24934                 type : 'hidden',
24935                 cls : 'roo-hidden-value',
24936                 value : this.checked ? this.inputValue : this.valueOff
24937             };
24938         }
24939         
24940             
24941         if (this.weight) { // Validity check?
24942             cfg.cls += " " + this.inputType + "-" + this.weight;
24943         }
24944         
24945         if (this.disabled) {
24946             input.disabled=true;
24947         }
24948         
24949         if(this.checked){
24950             input.checked = this.checked;
24951         }
24952         
24953         if (this.name) {
24954             
24955             input.name = this.name;
24956             
24957             if(this.inputType != 'radio'){
24958                 hidden.name = this.name;
24959                 input.name = '_hidden_' + this.name;
24960             }
24961         }
24962         
24963         if (this.size) {
24964             input.cls += ' input-' + this.size;
24965         }
24966         
24967         var settings=this;
24968         
24969         ['xs','sm','md','lg'].map(function(size){
24970             if (settings[size]) {
24971                 cfg.cls += ' col-' + size + '-' + settings[size];
24972             }
24973         });
24974         
24975         var inputblock = input;
24976          
24977         if (this.before || this.after) {
24978             
24979             inputblock = {
24980                 cls : 'input-group',
24981                 cn :  [] 
24982             };
24983             
24984             if (this.before) {
24985                 inputblock.cn.push({
24986                     tag :'span',
24987                     cls : 'input-group-addon',
24988                     html : this.before
24989                 });
24990             }
24991             
24992             inputblock.cn.push(input);
24993             
24994             if(this.inputType != 'radio'){
24995                 inputblock.cn.push(hidden);
24996             }
24997             
24998             if (this.after) {
24999                 inputblock.cn.push({
25000                     tag :'span',
25001                     cls : 'input-group-addon',
25002                     html : this.after
25003                 });
25004             }
25005             
25006         }
25007         var boxLabelCfg = false;
25008         
25009         if(this.boxLabel){
25010            
25011             boxLabelCfg = {
25012                 tag: 'label',
25013                 //'for': id, // box label is handled by onclick - so no for...
25014                 cls: 'box-label',
25015                 html: this.boxLabel
25016             };
25017             if(this.tooltip){
25018                 boxLabelCfg.tooltip = this.tooltip;
25019             }
25020              
25021         }
25022         
25023         
25024         if (align ==='left' && this.fieldLabel.length) {
25025 //                Roo.log("left and has label");
25026             cfg.cn = [
25027                 {
25028                     tag: 'label',
25029                     'for' :  id,
25030                     cls : 'control-label',
25031                     html : this.fieldLabel
25032                 },
25033                 {
25034                     cls : "", 
25035                     cn: [
25036                         inputblock
25037                     ]
25038                 }
25039             ];
25040             
25041             if (boxLabelCfg) {
25042                 cfg.cn[1].cn.push(boxLabelCfg);
25043             }
25044             
25045             if(this.labelWidth > 12){
25046                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25047             }
25048             
25049             if(this.labelWidth < 13 && this.labelmd == 0){
25050                 this.labelmd = this.labelWidth;
25051             }
25052             
25053             if(this.labellg > 0){
25054                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25055                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25056             }
25057             
25058             if(this.labelmd > 0){
25059                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25060                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25061             }
25062             
25063             if(this.labelsm > 0){
25064                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25065                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25066             }
25067             
25068             if(this.labelxs > 0){
25069                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25070                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25071             }
25072             
25073         } else if ( this.fieldLabel.length) {
25074 //                Roo.log(" label");
25075                 cfg.cn = [
25076                    
25077                     {
25078                         tag: this.boxLabel ? 'span' : 'label',
25079                         'for': id,
25080                         cls: 'control-label box-input-label',
25081                         //cls : 'input-group-addon',
25082                         html : this.fieldLabel
25083                     },
25084                     
25085                     inputblock
25086                     
25087                 ];
25088                 if (boxLabelCfg) {
25089                     cfg.cn.push(boxLabelCfg);
25090                 }
25091
25092         } else {
25093             
25094 //                Roo.log(" no label && no align");
25095                 cfg.cn = [  inputblock ] ;
25096                 if (boxLabelCfg) {
25097                     cfg.cn.push(boxLabelCfg);
25098                 }
25099
25100                 
25101         }
25102         
25103        
25104         
25105         if(this.inputType != 'radio'){
25106             cfg.cn.push(hidden);
25107         }
25108         
25109         return cfg;
25110         
25111     },
25112     
25113     /**
25114      * return the real input element.
25115      */
25116     inputEl: function ()
25117     {
25118         return this.el.select('input.roo-' + this.inputType,true).first();
25119     },
25120     hiddenEl: function ()
25121     {
25122         return this.el.select('input.roo-hidden-value',true).first();
25123     },
25124     
25125     labelEl: function()
25126     {
25127         return this.el.select('label.control-label',true).first();
25128     },
25129     /* depricated... */
25130     
25131     label: function()
25132     {
25133         return this.labelEl();
25134     },
25135     
25136     boxLabelEl: function()
25137     {
25138         return this.el.select('label.box-label',true).first();
25139     },
25140     
25141     initEvents : function()
25142     {
25143 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25144         
25145         this.inputEl().on('click', this.onClick,  this);
25146         
25147         if (this.boxLabel) { 
25148             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25149         }
25150         
25151         this.startValue = this.getValue();
25152         
25153         if(this.groupId){
25154             Roo.bootstrap.form.CheckBox.register(this);
25155         }
25156     },
25157     
25158     onClick : function(e)
25159     {   
25160         if(this.fireEvent('click', this, e) !== false){
25161             this.setChecked(!this.checked);
25162         }
25163         
25164     },
25165     
25166     setChecked : function(state,suppressEvent)
25167     {
25168         this.startValue = this.getValue();
25169
25170         if(this.inputType == 'radio'){
25171             
25172             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25173                 e.dom.checked = false;
25174             });
25175             
25176             this.inputEl().dom.checked = true;
25177             
25178             this.inputEl().dom.value = this.inputValue;
25179             
25180             if(suppressEvent !== true){
25181                 this.fireEvent('check', this, true);
25182             }
25183             
25184             this.validate();
25185             
25186             return;
25187         }
25188         
25189         this.checked = state;
25190         
25191         this.inputEl().dom.checked = state;
25192         
25193         
25194         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25195         
25196         if(suppressEvent !== true){
25197             this.fireEvent('check', this, state);
25198         }
25199         
25200         this.validate();
25201     },
25202     
25203     getValue : function()
25204     {
25205         if(this.inputType == 'radio'){
25206             return this.getGroupValue();
25207         }
25208         
25209         return this.hiddenEl().dom.value;
25210         
25211     },
25212     
25213     getGroupValue : function()
25214     {
25215         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25216             return '';
25217         }
25218         
25219         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25220     },
25221     
25222     setValue : function(v,suppressEvent)
25223     {
25224         if(this.inputType == 'radio'){
25225             this.setGroupValue(v, suppressEvent);
25226             return;
25227         }
25228         
25229         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25230         
25231         this.validate();
25232     },
25233     
25234     setGroupValue : function(v, suppressEvent)
25235     {
25236         this.startValue = this.getValue();
25237         
25238         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25239             e.dom.checked = false;
25240             
25241             if(e.dom.value == v){
25242                 e.dom.checked = true;
25243             }
25244         });
25245         
25246         if(suppressEvent !== true){
25247             this.fireEvent('check', this, true);
25248         }
25249
25250         this.validate();
25251         
25252         return;
25253     },
25254     
25255     validate : function()
25256     {
25257         if(this.getVisibilityEl().hasClass('hidden')){
25258             return true;
25259         }
25260         
25261         if(
25262                 this.disabled || 
25263                 (this.inputType == 'radio' && this.validateRadio()) ||
25264                 (this.inputType == 'checkbox' && this.validateCheckbox())
25265         ){
25266             this.markValid();
25267             return true;
25268         }
25269         
25270         this.markInvalid();
25271         return false;
25272     },
25273     
25274     validateRadio : function()
25275     {
25276         if(this.getVisibilityEl().hasClass('hidden')){
25277             return true;
25278         }
25279         
25280         if(this.allowBlank){
25281             return true;
25282         }
25283         
25284         var valid = false;
25285         
25286         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25287             if(!e.dom.checked){
25288                 return;
25289             }
25290             
25291             valid = true;
25292             
25293             return false;
25294         });
25295         
25296         return valid;
25297     },
25298     
25299     validateCheckbox : function()
25300     {
25301         if(!this.groupId){
25302             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25303             //return (this.getValue() == this.inputValue) ? true : false;
25304         }
25305         
25306         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25307         
25308         if(!group){
25309             return false;
25310         }
25311         
25312         var r = false;
25313         
25314         for(var i in group){
25315             if(group[i].el.isVisible(true)){
25316                 r = false;
25317                 break;
25318             }
25319             
25320             r = true;
25321         }
25322         
25323         for(var i in group){
25324             if(r){
25325                 break;
25326             }
25327             
25328             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25329         }
25330         
25331         return r;
25332     },
25333     
25334     /**
25335      * Mark this field as valid
25336      */
25337     markValid : function()
25338     {
25339         var _this = this;
25340         
25341         this.fireEvent('valid', this);
25342         
25343         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25344         
25345         if(this.groupId){
25346             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25347         }
25348         
25349         if(label){
25350             label.markValid();
25351         }
25352
25353         if(this.inputType == 'radio'){
25354             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25355                 var fg = e.findParent('.form-group', false, true);
25356                 if (Roo.bootstrap.version == 3) {
25357                     fg.removeClass([_this.invalidClass, _this.validClass]);
25358                     fg.addClass(_this.validClass);
25359                 } else {
25360                     fg.removeClass(['is-valid', 'is-invalid']);
25361                     fg.addClass('is-valid');
25362                 }
25363             });
25364             
25365             return;
25366         }
25367
25368         if(!this.groupId){
25369             var fg = this.el.findParent('.form-group', false, true);
25370             if (Roo.bootstrap.version == 3) {
25371                 fg.removeClass([this.invalidClass, this.validClass]);
25372                 fg.addClass(this.validClass);
25373             } else {
25374                 fg.removeClass(['is-valid', 'is-invalid']);
25375                 fg.addClass('is-valid');
25376             }
25377             return;
25378         }
25379         
25380         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25381         
25382         if(!group){
25383             return;
25384         }
25385         
25386         for(var i in group){
25387             var fg = group[i].el.findParent('.form-group', false, true);
25388             if (Roo.bootstrap.version == 3) {
25389                 fg.removeClass([this.invalidClass, this.validClass]);
25390                 fg.addClass(this.validClass);
25391             } else {
25392                 fg.removeClass(['is-valid', 'is-invalid']);
25393                 fg.addClass('is-valid');
25394             }
25395         }
25396     },
25397     
25398      /**
25399      * Mark this field as invalid
25400      * @param {String} msg The validation message
25401      */
25402     markInvalid : function(msg)
25403     {
25404         if(this.allowBlank){
25405             return;
25406         }
25407         
25408         var _this = this;
25409         
25410         this.fireEvent('invalid', this, msg);
25411         
25412         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25413         
25414         if(this.groupId){
25415             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25416         }
25417         
25418         if(label){
25419             label.markInvalid();
25420         }
25421             
25422         if(this.inputType == 'radio'){
25423             
25424             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25425                 var fg = e.findParent('.form-group', false, true);
25426                 if (Roo.bootstrap.version == 3) {
25427                     fg.removeClass([_this.invalidClass, _this.validClass]);
25428                     fg.addClass(_this.invalidClass);
25429                 } else {
25430                     fg.removeClass(['is-invalid', 'is-valid']);
25431                     fg.addClass('is-invalid');
25432                 }
25433             });
25434             
25435             return;
25436         }
25437         
25438         if(!this.groupId){
25439             var fg = this.el.findParent('.form-group', false, true);
25440             if (Roo.bootstrap.version == 3) {
25441                 fg.removeClass([_this.invalidClass, _this.validClass]);
25442                 fg.addClass(_this.invalidClass);
25443             } else {
25444                 fg.removeClass(['is-invalid', 'is-valid']);
25445                 fg.addClass('is-invalid');
25446             }
25447             return;
25448         }
25449         
25450         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25451         
25452         if(!group){
25453             return;
25454         }
25455         
25456         for(var i in group){
25457             var fg = group[i].el.findParent('.form-group', false, true);
25458             if (Roo.bootstrap.version == 3) {
25459                 fg.removeClass([_this.invalidClass, _this.validClass]);
25460                 fg.addClass(_this.invalidClass);
25461             } else {
25462                 fg.removeClass(['is-invalid', 'is-valid']);
25463                 fg.addClass('is-invalid');
25464             }
25465         }
25466         
25467     },
25468     
25469     clearInvalid : function()
25470     {
25471         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25472         
25473         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25474         
25475         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25476         
25477         if (label && label.iconEl) {
25478             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25479             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25480         }
25481     },
25482     
25483     disable : function()
25484     {
25485         if(this.inputType != 'radio'){
25486             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25487             return;
25488         }
25489         
25490         var _this = this;
25491         
25492         if(this.rendered){
25493             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25494                 _this.getActionEl().addClass(this.disabledClass);
25495                 e.dom.disabled = true;
25496             });
25497         }
25498         
25499         this.disabled = true;
25500         this.fireEvent("disable", this);
25501         return this;
25502     },
25503
25504     enable : function()
25505     {
25506         if(this.inputType != 'radio'){
25507             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25508             return;
25509         }
25510         
25511         var _this = this;
25512         
25513         if(this.rendered){
25514             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25515                 _this.getActionEl().removeClass(this.disabledClass);
25516                 e.dom.disabled = false;
25517             });
25518         }
25519         
25520         this.disabled = false;
25521         this.fireEvent("enable", this);
25522         return this;
25523     },
25524     
25525     setBoxLabel : function(v)
25526     {
25527         this.boxLabel = v;
25528         
25529         if(this.rendered){
25530             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25531         }
25532     }
25533
25534 });
25535
25536 Roo.apply(Roo.bootstrap.form.CheckBox, {
25537     
25538     groups: {},
25539     
25540      /**
25541     * register a CheckBox Group
25542     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25543     */
25544     register : function(checkbox)
25545     {
25546         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25547             this.groups[checkbox.groupId] = {};
25548         }
25549         
25550         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25551             return;
25552         }
25553         
25554         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25555         
25556     },
25557     /**
25558     * fetch a CheckBox Group based on the group ID
25559     * @param {string} the group ID
25560     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25561     */
25562     get: function(groupId) {
25563         if (typeof(this.groups[groupId]) == 'undefined') {
25564             return false;
25565         }
25566         
25567         return this.groups[groupId] ;
25568     }
25569     
25570     
25571 });
25572 /*
25573  * - LGPL
25574  *
25575  * RadioItem
25576  * 
25577  */
25578
25579 /**
25580  * @class Roo.bootstrap.form.Radio
25581  * @extends Roo.bootstrap.Component
25582  * Bootstrap Radio class
25583  * @cfg {String} boxLabel - the label associated
25584  * @cfg {String} value - the value of radio
25585  * 
25586  * @constructor
25587  * Create a new Radio
25588  * @param {Object} config The config object
25589  */
25590 Roo.bootstrap.form.Radio = function(config){
25591     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25592     
25593 };
25594
25595 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25596     
25597     boxLabel : '',
25598     
25599     value : '',
25600     
25601     getAutoCreate : function()
25602     {
25603         var cfg = {
25604             tag : 'div',
25605             cls : 'form-group radio',
25606             cn : [
25607                 {
25608                     tag : 'label',
25609                     cls : 'box-label',
25610                     html : this.boxLabel
25611                 }
25612             ]
25613         };
25614         
25615         return cfg;
25616     },
25617     
25618     initEvents : function() 
25619     {
25620         this.parent().register(this);
25621         
25622         this.el.on('click', this.onClick, this);
25623         
25624     },
25625     
25626     onClick : function(e)
25627     {
25628         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25629             this.setChecked(true);
25630         }
25631     },
25632     
25633     setChecked : function(state, suppressEvent)
25634     {
25635         this.parent().setValue(this.value, suppressEvent);
25636         
25637     },
25638     
25639     setBoxLabel : function(v)
25640     {
25641         this.boxLabel = v;
25642         
25643         if(this.rendered){
25644             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25645         }
25646     }
25647     
25648 });
25649  
25650
25651  /*
25652  * - LGPL
25653  *
25654  * Input
25655  * 
25656  */
25657
25658 /**
25659  * @class Roo.bootstrap.form.SecurePass
25660  * @extends Roo.bootstrap.form.Input
25661  * Bootstrap SecurePass class
25662  *
25663  * 
25664  * @constructor
25665  * Create a new SecurePass
25666  * @param {Object} config The config object
25667  */
25668  
25669 Roo.bootstrap.form.SecurePass = function (config) {
25670     // these go here, so the translation tool can replace them..
25671     this.errors = {
25672         PwdEmpty: "Please type a password, and then retype it to confirm.",
25673         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25674         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25675         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25676         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25677         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25678         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25679         TooWeak: "Your password is Too Weak."
25680     },
25681     this.meterLabel = "Password strength:";
25682     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25683     this.meterClass = [
25684         "roo-password-meter-tooweak", 
25685         "roo-password-meter-weak", 
25686         "roo-password-meter-medium", 
25687         "roo-password-meter-strong", 
25688         "roo-password-meter-grey"
25689     ];
25690     
25691     this.errors = {};
25692     
25693     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25694 }
25695
25696 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25697     /**
25698      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25699      * {
25700      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25701      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25702      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25703      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25704      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25705      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25706      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25707      * })
25708      */
25709     // private
25710     
25711     meterWidth: 300,
25712     errorMsg :'',    
25713     errors: false,
25714     imageRoot: '/',
25715     /**
25716      * @cfg {String/Object} Label for the strength meter (defaults to
25717      * 'Password strength:')
25718      */
25719     // private
25720     meterLabel: '',
25721     /**
25722      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25723      * ['Weak', 'Medium', 'Strong'])
25724      */
25725     // private    
25726     pwdStrengths: false,    
25727     // private
25728     strength: 0,
25729     // private
25730     _lastPwd: null,
25731     // private
25732     kCapitalLetter: 0,
25733     kSmallLetter: 1,
25734     kDigit: 2,
25735     kPunctuation: 3,
25736     
25737     insecure: false,
25738     // private
25739     initEvents: function ()
25740     {
25741         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25742
25743         if (this.el.is('input[type=password]') && Roo.isSafari) {
25744             this.el.on('keydown', this.SafariOnKeyDown, this);
25745         }
25746
25747         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25748     },
25749     // private
25750     onRender: function (ct, position)
25751     {
25752         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25753         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25754         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25755
25756         this.trigger.createChild({
25757                    cn: [
25758                     {
25759                     //id: 'PwdMeter',
25760                     tag: 'div',
25761                     cls: 'roo-password-meter-grey col-xs-12',
25762                     style: {
25763                         //width: 0,
25764                         //width: this.meterWidth + 'px'                                                
25765                         }
25766                     },
25767                     {                            
25768                          cls: 'roo-password-meter-text'                          
25769                     }
25770                 ]            
25771         });
25772
25773          
25774         if (this.hideTrigger) {
25775             this.trigger.setDisplayed(false);
25776         }
25777         this.setSize(this.width || '', this.height || '');
25778     },
25779     // private
25780     onDestroy: function ()
25781     {
25782         if (this.trigger) {
25783             this.trigger.removeAllListeners();
25784             this.trigger.remove();
25785         }
25786         if (this.wrap) {
25787             this.wrap.remove();
25788         }
25789         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25790     },
25791     // private
25792     checkStrength: function ()
25793     {
25794         var pwd = this.inputEl().getValue();
25795         if (pwd == this._lastPwd) {
25796             return;
25797         }
25798
25799         var strength;
25800         if (this.ClientSideStrongPassword(pwd)) {
25801             strength = 3;
25802         } else if (this.ClientSideMediumPassword(pwd)) {
25803             strength = 2;
25804         } else if (this.ClientSideWeakPassword(pwd)) {
25805             strength = 1;
25806         } else {
25807             strength = 0;
25808         }
25809         
25810         Roo.log('strength1: ' + strength);
25811         
25812         //var pm = this.trigger.child('div/div/div').dom;
25813         var pm = this.trigger.child('div/div');
25814         pm.removeClass(this.meterClass);
25815         pm.addClass(this.meterClass[strength]);
25816                 
25817         
25818         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25819                 
25820         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25821         
25822         this._lastPwd = pwd;
25823     },
25824     reset: function ()
25825     {
25826         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25827         
25828         this._lastPwd = '';
25829         
25830         var pm = this.trigger.child('div/div');
25831         pm.removeClass(this.meterClass);
25832         pm.addClass('roo-password-meter-grey');        
25833         
25834         
25835         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25836         
25837         pt.innerHTML = '';
25838         this.inputEl().dom.type='password';
25839     },
25840     // private
25841     validateValue: function (value)
25842     {
25843         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25844             return false;
25845         }
25846         if (value.length == 0) {
25847             if (this.allowBlank) {
25848                 this.clearInvalid();
25849                 return true;
25850             }
25851
25852             this.markInvalid(this.errors.PwdEmpty);
25853             this.errorMsg = this.errors.PwdEmpty;
25854             return false;
25855         }
25856         
25857         if(this.insecure){
25858             return true;
25859         }
25860         
25861         if (!value.match(/[\x21-\x7e]+/)) {
25862             this.markInvalid(this.errors.PwdBadChar);
25863             this.errorMsg = this.errors.PwdBadChar;
25864             return false;
25865         }
25866         if (value.length < 6) {
25867             this.markInvalid(this.errors.PwdShort);
25868             this.errorMsg = this.errors.PwdShort;
25869             return false;
25870         }
25871         if (value.length > 16) {
25872             this.markInvalid(this.errors.PwdLong);
25873             this.errorMsg = this.errors.PwdLong;
25874             return false;
25875         }
25876         var strength;
25877         if (this.ClientSideStrongPassword(value)) {
25878             strength = 3;
25879         } else if (this.ClientSideMediumPassword(value)) {
25880             strength = 2;
25881         } else if (this.ClientSideWeakPassword(value)) {
25882             strength = 1;
25883         } else {
25884             strength = 0;
25885         }
25886
25887         
25888         if (strength < 2) {
25889             //this.markInvalid(this.errors.TooWeak);
25890             this.errorMsg = this.errors.TooWeak;
25891             //return false;
25892         }
25893         
25894         
25895         console.log('strength2: ' + strength);
25896         
25897         //var pm = this.trigger.child('div/div/div').dom;
25898         
25899         var pm = this.trigger.child('div/div');
25900         pm.removeClass(this.meterClass);
25901         pm.addClass(this.meterClass[strength]);
25902                 
25903         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25904                 
25905         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25906         
25907         this.errorMsg = ''; 
25908         return true;
25909     },
25910     // private
25911     CharacterSetChecks: function (type)
25912     {
25913         this.type = type;
25914         this.fResult = false;
25915     },
25916     // private
25917     isctype: function (character, type)
25918     {
25919         switch (type) {  
25920             case this.kCapitalLetter:
25921                 if (character >= 'A' && character <= 'Z') {
25922                     return true;
25923                 }
25924                 break;
25925             
25926             case this.kSmallLetter:
25927                 if (character >= 'a' && character <= 'z') {
25928                     return true;
25929                 }
25930                 break;
25931             
25932             case this.kDigit:
25933                 if (character >= '0' && character <= '9') {
25934                     return true;
25935                 }
25936                 break;
25937             
25938             case this.kPunctuation:
25939                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25940                     return true;
25941                 }
25942                 break;
25943             
25944             default:
25945                 return false;
25946         }
25947
25948     },
25949     // private
25950     IsLongEnough: function (pwd, size)
25951     {
25952         return !(pwd == null || isNaN(size) || pwd.length < size);
25953     },
25954     // private
25955     SpansEnoughCharacterSets: function (word, nb)
25956     {
25957         if (!this.IsLongEnough(word, nb))
25958         {
25959             return false;
25960         }
25961
25962         var characterSetChecks = new Array(
25963             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25964             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25965         );
25966         
25967         for (var index = 0; index < word.length; ++index) {
25968             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25969                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25970                     characterSetChecks[nCharSet].fResult = true;
25971                     break;
25972                 }
25973             }
25974         }
25975
25976         var nCharSets = 0;
25977         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25978             if (characterSetChecks[nCharSet].fResult) {
25979                 ++nCharSets;
25980             }
25981         }
25982
25983         if (nCharSets < nb) {
25984             return false;
25985         }
25986         return true;
25987     },
25988     // private
25989     ClientSideStrongPassword: function (pwd)
25990     {
25991         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25992     },
25993     // private
25994     ClientSideMediumPassword: function (pwd)
25995     {
25996         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25997     },
25998     // private
25999     ClientSideWeakPassword: function (pwd)
26000     {
26001         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26002     }
26003           
26004 });
26005 Roo.htmleditor = {};
26006  
26007 /**
26008  * @class Roo.htmleditor.Filter
26009  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26010  * @cfg {DomElement} node The node to iterate and filter
26011  * @cfg {boolean|String|Array} tag Tags to replace 
26012  * @constructor
26013  * Create a new Filter.
26014  * @param {Object} config Configuration options
26015  */
26016
26017
26018
26019 Roo.htmleditor.Filter = function(cfg) {
26020     Roo.apply(this.cfg);
26021     // this does not actually call walk as it's really just a abstract class
26022 }
26023
26024
26025 Roo.htmleditor.Filter.prototype = {
26026     
26027     node: false,
26028     
26029     tag: false,
26030
26031     // overrride to do replace comments.
26032     replaceComment : false,
26033     
26034     // overrride to do replace or do stuff with tags..
26035     replaceTag : false,
26036     
26037     walk : function(dom)
26038     {
26039         Roo.each( Array.from(dom.childNodes), function( e ) {
26040             switch(true) {
26041                 
26042                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26043                     this.replaceComment(e);
26044                     return;
26045                 
26046                 case e.nodeType != 1: //not a node.
26047                     return;
26048                 
26049                 case this.tag === true: // everything
26050                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26051                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26052                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26053                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26054                     if (this.replaceTag && false === this.replaceTag(e)) {
26055                         return;
26056                     }
26057                     if (e.hasChildNodes()) {
26058                         this.walk(e);
26059                     }
26060                     return;
26061                 
26062                 default:    // tags .. that do not match.
26063                     if (e.hasChildNodes()) {
26064                         this.walk(e);
26065                     }
26066             }
26067             
26068         }, this);
26069         
26070     },
26071     
26072     
26073     removeNodeKeepChildren : function( node)
26074     {
26075     
26076         ar = Array.from(node.childNodes);
26077         for (var i = 0; i < ar.length; i++) {
26078          
26079             node.removeChild(ar[i]);
26080             // what if we need to walk these???
26081             node.parentNode.insertBefore(ar[i], node);
26082            
26083         }
26084         node.parentNode.removeChild(node);
26085     }
26086 }; 
26087
26088 /**
26089  * @class Roo.htmleditor.FilterAttributes
26090  * clean attributes and  styles including http:// etc.. in attribute
26091  * @constructor
26092 * Run a new Attribute Filter
26093 * @param {Object} config Configuration options
26094  */
26095 Roo.htmleditor.FilterAttributes = function(cfg)
26096 {
26097     Roo.apply(this, cfg);
26098     this.attrib_black = this.attrib_black || [];
26099     this.attrib_white = this.attrib_white || [];
26100
26101     this.attrib_clean = this.attrib_clean || [];
26102     this.style_white = this.style_white || [];
26103     this.style_black = this.style_black || [];
26104     this.walk(cfg.node);
26105 }
26106
26107 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26108 {
26109     tag: true, // all tags
26110     
26111     attrib_black : false, // array
26112     attrib_clean : false,
26113     attrib_white : false,
26114
26115     style_white : false,
26116     style_black : false,
26117      
26118      
26119     replaceTag : function(node)
26120     {
26121         if (!node.attributes || !node.attributes.length) {
26122             return true;
26123         }
26124         
26125         for (var i = node.attributes.length-1; i > -1 ; i--) {
26126             var a = node.attributes[i];
26127             //console.log(a);
26128             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26129                 node.removeAttribute(a.name);
26130                 continue;
26131             }
26132             
26133             
26134             
26135             if (a.name.toLowerCase().substr(0,2)=='on')  {
26136                 node.removeAttribute(a.name);
26137                 continue;
26138             }
26139             
26140             
26141             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26142                 node.removeAttribute(a.name);
26143                 continue;
26144             }
26145             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26146                 this.cleanAttr(node,a.name,a.value); // fixme..
26147                 continue;
26148             }
26149             if (a.name == 'style') {
26150                 this.cleanStyle(node,a.name,a.value);
26151                 continue;
26152             }
26153             /// clean up MS crap..
26154             // tecnically this should be a list of valid class'es..
26155             
26156             
26157             if (a.name == 'class') {
26158                 if (a.value.match(/^Mso/)) {
26159                     node.removeAttribute('class');
26160                 }
26161                 
26162                 if (a.value.match(/^body$/)) {
26163                     node.removeAttribute('class');
26164                 }
26165                 continue;
26166             }
26167             
26168             
26169             // style cleanup!?
26170             // class cleanup?
26171             
26172         }
26173         return true; // clean children
26174     },
26175         
26176     cleanAttr: function(node, n,v)
26177     {
26178         
26179         if (v.match(/^\./) || v.match(/^\//)) {
26180             return;
26181         }
26182         if (v.match(/^(http|https):\/\//)
26183             || v.match(/^mailto:/) 
26184             || v.match(/^ftp:/)
26185             || v.match(/^data:/)
26186             ) {
26187             return;
26188         }
26189         if (v.match(/^#/)) {
26190             return;
26191         }
26192         if (v.match(/^\{/)) { // allow template editing.
26193             return;
26194         }
26195 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26196         node.removeAttribute(n);
26197         
26198     },
26199     cleanStyle : function(node,  n,v)
26200     {
26201         if (v.match(/expression/)) { //XSS?? should we even bother..
26202             node.removeAttribute(n);
26203             return;
26204         }
26205         
26206         var parts = v.split(/;/);
26207         var clean = [];
26208         
26209         Roo.each(parts, function(p) {
26210             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26211             if (!p.length) {
26212                 return true;
26213             }
26214             var l = p.split(':').shift().replace(/\s+/g,'');
26215             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26216             
26217             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26218                 return true;
26219             }
26220             //Roo.log()
26221             // only allow 'c whitelisted system attributes'
26222             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26223                 return true;
26224             }
26225             
26226             
26227             clean.push(p);
26228             return true;
26229         },this);
26230         if (clean.length) { 
26231             node.setAttribute(n, clean.join(';'));
26232         } else {
26233             node.removeAttribute(n);
26234         }
26235         
26236     }
26237         
26238         
26239         
26240     
26241 });/**
26242  * @class Roo.htmleditor.FilterBlack
26243  * remove blacklisted elements.
26244  * @constructor
26245  * Run a new Blacklisted Filter
26246  * @param {Object} config Configuration options
26247  */
26248
26249 Roo.htmleditor.FilterBlack = function(cfg)
26250 {
26251     Roo.apply(this, cfg);
26252     this.walk(cfg.node);
26253 }
26254
26255 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26256 {
26257     tag : true, // all elements.
26258    
26259     replaceTag : function(n)
26260     {
26261         n.parentNode.removeChild(n);
26262     }
26263 });
26264 /**
26265  * @class Roo.htmleditor.FilterComment
26266  * remove comments.
26267  * @constructor
26268 * Run a new Comments Filter
26269 * @param {Object} config Configuration options
26270  */
26271 Roo.htmleditor.FilterComment = function(cfg)
26272 {
26273     this.walk(cfg.node);
26274 }
26275
26276 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26277 {
26278   
26279     replaceComment : function(n)
26280     {
26281         n.parentNode.removeChild(n);
26282     }
26283 });/**
26284  * @class Roo.htmleditor.FilterKeepChildren
26285  * remove tags but keep children
26286  * @constructor
26287  * Run a new Keep Children Filter
26288  * @param {Object} config Configuration options
26289  */
26290
26291 Roo.htmleditor.FilterKeepChildren = function(cfg)
26292 {
26293     Roo.apply(this, cfg);
26294     if (this.tag === false) {
26295         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26296     }
26297     // hacky?
26298     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26299         this.cleanNamespace = true;
26300     }
26301         
26302     this.walk(cfg.node);
26303 }
26304
26305 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26306 {
26307     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26308   
26309     replaceTag : function(node)
26310     {
26311         // walk children...
26312         //Roo.log(node.tagName);
26313         var ar = Array.from(node.childNodes);
26314         //remove first..
26315         
26316         for (var i = 0; i < ar.length; i++) {
26317             var e = ar[i];
26318             if (e.nodeType == 1) {
26319                 if (
26320                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26321                     || // array and it matches
26322                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26323                     ||
26324                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26325                     ||
26326                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26327                 ) {
26328                     this.replaceTag(ar[i]); // child is blacklisted as well...
26329                     continue;
26330                 }
26331             }
26332         }  
26333         ar = Array.from(node.childNodes);
26334         for (var i = 0; i < ar.length; i++) {
26335          
26336             node.removeChild(ar[i]);
26337             // what if we need to walk these???
26338             node.parentNode.insertBefore(ar[i], node);
26339             if (this.tag !== false) {
26340                 this.walk(ar[i]);
26341                 
26342             }
26343         }
26344         //Roo.log("REMOVE:" + node.tagName);
26345         node.parentNode.removeChild(node);
26346         return false; // don't walk children
26347         
26348         
26349     }
26350 });/**
26351  * @class Roo.htmleditor.FilterParagraph
26352  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26353  * like on 'push' to remove the <p> tags and replace them with line breaks.
26354  * @constructor
26355  * Run a new Paragraph Filter
26356  * @param {Object} config Configuration options
26357  */
26358
26359 Roo.htmleditor.FilterParagraph = function(cfg)
26360 {
26361     // no need to apply config.
26362     this.walk(cfg.node);
26363 }
26364
26365 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26366 {
26367     
26368      
26369     tag : 'P',
26370     
26371      
26372     replaceTag : function(node)
26373     {
26374         
26375         if (node.childNodes.length == 1 &&
26376             node.childNodes[0].nodeType == 3 &&
26377             node.childNodes[0].textContent.trim().length < 1
26378             ) {
26379             // remove and replace with '<BR>';
26380             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26381             return false; // no need to walk..
26382         }
26383         var ar = Array.from(node.childNodes);
26384         for (var i = 0; i < ar.length; i++) {
26385             node.removeChild(ar[i]);
26386             // what if we need to walk these???
26387             node.parentNode.insertBefore(ar[i], node);
26388         }
26389         // now what about this?
26390         // <p> &nbsp; </p>
26391         
26392         // double BR.
26393         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26394         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26395         node.parentNode.removeChild(node);
26396         
26397         return false;
26398
26399     }
26400     
26401 });/**
26402  * @class Roo.htmleditor.FilterSpan
26403  * filter span's with no attributes out..
26404  * @constructor
26405  * Run a new Span Filter
26406  * @param {Object} config Configuration options
26407  */
26408
26409 Roo.htmleditor.FilterSpan = function(cfg)
26410 {
26411     // no need to apply config.
26412     this.walk(cfg.node);
26413 }
26414
26415 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26416 {
26417      
26418     tag : 'SPAN',
26419      
26420  
26421     replaceTag : function(node)
26422     {
26423         if (node.attributes && node.attributes.length > 0) {
26424             return true; // walk if there are any.
26425         }
26426         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26427         return false;
26428      
26429     }
26430     
26431 });/**
26432  * @class Roo.htmleditor.FilterTableWidth
26433   try and remove table width data - as that frequently messes up other stuff.
26434  * 
26435  *      was cleanTableWidths.
26436  *
26437  * Quite often pasting from word etc.. results in tables with column and widths.
26438  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26439  *
26440  * @constructor
26441  * Run a new Table Filter
26442  * @param {Object} config Configuration options
26443  */
26444
26445 Roo.htmleditor.FilterTableWidth = function(cfg)
26446 {
26447     // no need to apply config.
26448     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26449     this.walk(cfg.node);
26450 }
26451
26452 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26453 {
26454      
26455      
26456     
26457     replaceTag: function(node) {
26458         
26459         
26460       
26461         if (node.hasAttribute('width')) {
26462             node.removeAttribute('width');
26463         }
26464         
26465          
26466         if (node.hasAttribute("style")) {
26467             // pretty basic...
26468             
26469             var styles = node.getAttribute("style").split(";");
26470             var nstyle = [];
26471             Roo.each(styles, function(s) {
26472                 if (!s.match(/:/)) {
26473                     return;
26474                 }
26475                 var kv = s.split(":");
26476                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26477                     return;
26478                 }
26479                 // what ever is left... we allow.
26480                 nstyle.push(s);
26481             });
26482             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26483             if (!nstyle.length) {
26484                 node.removeAttribute('style');
26485             }
26486         }
26487         
26488         return true; // continue doing children..
26489     }
26490 });/**
26491  * @class Roo.htmleditor.FilterWord
26492  * try and clean up all the mess that Word generates.
26493  * 
26494  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26495  
26496  * @constructor
26497  * Run a new Span Filter
26498  * @param {Object} config Configuration options
26499  */
26500
26501 Roo.htmleditor.FilterWord = function(cfg)
26502 {
26503     // no need to apply config.
26504     this.replaceDocBullets(cfg.node);
26505     
26506     this.replaceAname(cfg.node);
26507     // this is disabled as the removal is done by other filters;
26508    // this.walk(cfg.node);
26509     
26510     
26511 }
26512
26513 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26514 {
26515     tag: true,
26516      
26517     
26518     /**
26519      * Clean up MS wordisms...
26520      */
26521     replaceTag : function(node)
26522     {
26523          
26524         // no idea what this does - span with text, replaceds with just text.
26525         if(
26526                 node.nodeName == 'SPAN' &&
26527                 !node.hasAttributes() &&
26528                 node.childNodes.length == 1 &&
26529                 node.firstChild.nodeName == "#text"  
26530         ) {
26531             var textNode = node.firstChild;
26532             node.removeChild(textNode);
26533             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26534                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26535             }
26536             node.parentNode.insertBefore(textNode, node);
26537             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26538                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26539             }
26540             
26541             node.parentNode.removeChild(node);
26542             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26543         }
26544         
26545    
26546         
26547         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26548             node.parentNode.removeChild(node);
26549             return false; // dont do chidlren
26550         }
26551         //Roo.log(node.tagName);
26552         // remove - but keep children..
26553         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26554             //Roo.log('-- removed');
26555             while (node.childNodes.length) {
26556                 var cn = node.childNodes[0];
26557                 node.removeChild(cn);
26558                 node.parentNode.insertBefore(cn, node);
26559                 // move node to parent - and clean it..
26560                 if (cn.nodeType == 1) {
26561                     this.replaceTag(cn);
26562                 }
26563                 
26564             }
26565             node.parentNode.removeChild(node);
26566             /// no need to iterate chidlren = it's got none..
26567             //this.iterateChildren(node, this.cleanWord);
26568             return false; // no need to iterate children.
26569         }
26570         // clean styles
26571         if (node.className.length) {
26572             
26573             var cn = node.className.split(/\W+/);
26574             var cna = [];
26575             Roo.each(cn, function(cls) {
26576                 if (cls.match(/Mso[a-zA-Z]+/)) {
26577                     return;
26578                 }
26579                 cna.push(cls);
26580             });
26581             node.className = cna.length ? cna.join(' ') : '';
26582             if (!cna.length) {
26583                 node.removeAttribute("class");
26584             }
26585         }
26586         
26587         if (node.hasAttribute("lang")) {
26588             node.removeAttribute("lang");
26589         }
26590         
26591         if (node.hasAttribute("style")) {
26592             
26593             var styles = node.getAttribute("style").split(";");
26594             var nstyle = [];
26595             Roo.each(styles, function(s) {
26596                 if (!s.match(/:/)) {
26597                     return;
26598                 }
26599                 var kv = s.split(":");
26600                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26601                     return;
26602                 }
26603                 // what ever is left... we allow.
26604                 nstyle.push(s);
26605             });
26606             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26607             if (!nstyle.length) {
26608                 node.removeAttribute('style');
26609             }
26610         }
26611         return true; // do children
26612         
26613         
26614         
26615     },
26616     
26617     styleToObject: function(node)
26618     {
26619         var styles = (node.getAttribute("style") || '').split(";");
26620         var ret = {};
26621         Roo.each(styles, function(s) {
26622             if (!s.match(/:/)) {
26623                 return;
26624             }
26625             var kv = s.split(":");
26626              
26627             // what ever is left... we allow.
26628             ret[kv[0].trim()] = kv[1];
26629         });
26630         return ret;
26631     },
26632     
26633     
26634     replaceAname : function (doc)
26635     {
26636         // replace all the a/name without..
26637         var aa = Array.from(doc.getElementsByTagName('a'));
26638         for (var i = 0; i  < aa.length; i++) {
26639             var a = aa[i];
26640             if (a.hasAttribute("name")) {
26641                 a.removeAttribute("name");
26642             }
26643             if (a.hasAttribute("href")) {
26644                 continue;
26645             }
26646             // reparent children.
26647             this.removeNodeKeepChildren(a);
26648             
26649         }
26650         
26651         
26652         
26653     },
26654
26655     
26656     
26657     replaceDocBullets : function(doc)
26658     {
26659         // this is a bit odd - but it appears some indents use ql-indent-1
26660          //Roo.log(doc.innerHTML);
26661         
26662         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26663         for( var i = 0; i < listpara.length; i ++) {
26664             listpara[i].className = "MsoListParagraph";
26665         }
26666         
26667         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26668         for( var i = 0; i < listpara.length; i ++) {
26669             listpara[i].className = "MsoListParagraph";
26670         }
26671         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26672         for( var i = 0; i < listpara.length; i ++) {
26673             listpara[i].className = "MsoListParagraph";
26674         }
26675         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26676         for( var i = 0; i < listpara.length; i ++) {
26677             listpara[i].className = "MsoListParagraph";
26678         }
26679         
26680         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26681         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26682         for( var i = 0; i < htwo.length; i ++) {
26683             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26684                 htwo[i].className = "MsoListParagraph";
26685             }
26686         }
26687         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26688         for( var i = 0; i < listpara.length; i ++) {
26689             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26690                 listpara[i].className = "MsoListParagraph";
26691             } else {
26692                 listpara[i].className = "MsoNormalx";
26693             }
26694         }
26695        
26696         listpara = doc.getElementsByClassName('MsoListParagraph');
26697         // Roo.log(doc.innerHTML);
26698         
26699         
26700         
26701         while(listpara.length) {
26702             
26703             this.replaceDocBullet(listpara.item(0));
26704         }
26705       
26706     },
26707     
26708      
26709     
26710     replaceDocBullet : function(p)
26711     {
26712         // gather all the siblings.
26713         var ns = p,
26714             parent = p.parentNode,
26715             doc = parent.ownerDocument,
26716             items = [];
26717             
26718         var listtype = 'ul';   
26719         while (ns) {
26720             if (ns.nodeType != 1) {
26721                 ns = ns.nextSibling;
26722                 continue;
26723             }
26724             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26725                 break;
26726             }
26727             var spans = ns.getElementsByTagName('span');
26728             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26729                 items.push(ns);
26730                 ns = ns.nextSibling;
26731                 has_list = true;
26732                 if (spans.length && spans[0].hasAttribute('style')) {
26733                     var  style = this.styleToObject(spans[0]);
26734                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26735                         listtype = 'ol';
26736                     }
26737                 }
26738                 
26739                 continue;
26740             }
26741             var spans = ns.getElementsByTagName('span');
26742             if (!spans.length) {
26743                 break;
26744             }
26745             var has_list  = false;
26746             for(var i = 0; i < spans.length; i++) {
26747                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26748                     has_list = true;
26749                     break;
26750                 }
26751             }
26752             if (!has_list) {
26753                 break;
26754             }
26755             items.push(ns);
26756             ns = ns.nextSibling;
26757             
26758             
26759         }
26760         if (!items.length) {
26761             ns.className = "";
26762             return;
26763         }
26764         
26765         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26766         parent.insertBefore(ul, p);
26767         var lvl = 0;
26768         var stack = [ ul ];
26769         var last_li = false;
26770         
26771         var margin_to_depth = {};
26772         max_margins = -1;
26773         
26774         items.forEach(function(n, ipos) {
26775             //Roo.log("got innertHMLT=" + n.innerHTML);
26776             
26777             var spans = n.getElementsByTagName('span');
26778             if (!spans.length) {
26779                 //Roo.log("No spans found");
26780                  
26781                 parent.removeChild(n);
26782                 
26783                 
26784                 return; // skip it...
26785             }
26786            
26787                 
26788             var num = 1;
26789             var style = {};
26790             for(var i = 0; i < spans.length; i++) {
26791             
26792                 style = this.styleToObject(spans[i]);
26793                 if (typeof(style['mso-list']) == 'undefined') {
26794                     continue;
26795                 }
26796                 if (listtype == 'ol') {
26797                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26798                 }
26799                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26800                 break;
26801             }
26802             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26803             style = this.styleToObject(n); // mo-list is from the parent node.
26804             if (typeof(style['mso-list']) == 'undefined') {
26805                 //Roo.log("parent is missing level");
26806                   
26807                 parent.removeChild(n);
26808                  
26809                 return;
26810             }
26811             
26812             var margin = style['margin-left'];
26813             if (typeof(margin_to_depth[margin]) == 'undefined') {
26814                 max_margins++;
26815                 margin_to_depth[margin] = max_margins;
26816             }
26817             nlvl = margin_to_depth[margin] ;
26818              
26819             if (nlvl > lvl) {
26820                 //new indent
26821                 var nul = doc.createElement(listtype); // what about number lists...
26822                 if (!last_li) {
26823                     last_li = doc.createElement('li');
26824                     stack[lvl].appendChild(last_li);
26825                 }
26826                 last_li.appendChild(nul);
26827                 stack[nlvl] = nul;
26828                 
26829             }
26830             lvl = nlvl;
26831             
26832             // not starting at 1..
26833             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26834                 stack[nlvl].setAttribute("start", num);
26835             }
26836             
26837             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26838             last_li = nli;
26839             nli.innerHTML = n.innerHTML;
26840             //Roo.log("innerHTML = " + n.innerHTML);
26841             parent.removeChild(n);
26842             
26843              
26844              
26845             
26846         },this);
26847         
26848         
26849         
26850         
26851     }
26852     
26853     
26854     
26855 });
26856 /**
26857  * @class Roo.htmleditor.FilterStyleToTag
26858  * part of the word stuff... - certain 'styles' should be converted to tags.
26859  * eg.
26860  *   font-weight: bold -> bold
26861  *   ?? super / subscrit etc..
26862  * 
26863  * @constructor
26864 * Run a new style to tag filter.
26865 * @param {Object} config Configuration options
26866  */
26867 Roo.htmleditor.FilterStyleToTag = function(cfg)
26868 {
26869     
26870     this.tags = {
26871         B  : [ 'fontWeight' , 'bold'],
26872         I :  [ 'fontStyle' , 'italic'],
26873         //pre :  [ 'font-style' , 'italic'],
26874         // h1.. h6 ?? font-size?
26875         SUP : [ 'verticalAlign' , 'super' ],
26876         SUB : [ 'verticalAlign' , 'sub' ]
26877         
26878         
26879     };
26880     
26881     Roo.apply(this, cfg);
26882      
26883     
26884     this.walk(cfg.node);
26885     
26886     
26887     
26888 }
26889
26890
26891 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26892 {
26893     tag: true, // all tags
26894     
26895     tags : false,
26896     
26897     
26898     replaceTag : function(node)
26899     {
26900         
26901         
26902         if (node.getAttribute("style") === null) {
26903             return true;
26904         }
26905         var inject = [];
26906         for (var k in this.tags) {
26907             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26908                 inject.push(k);
26909                 node.style.removeProperty(this.tags[k][0]);
26910             }
26911         }
26912         if (!inject.length) {
26913             return true; 
26914         }
26915         var cn = Array.from(node.childNodes);
26916         var nn = node;
26917         Roo.each(inject, function(t) {
26918             var nc = node.ownerDocument.createElement(t);
26919             nn.appendChild(nc);
26920             nn = nc;
26921         });
26922         for(var i = 0;i < cn.length;cn++) {
26923             node.removeChild(cn[i]);
26924             nn.appendChild(cn[i]);
26925         }
26926         return true /// iterate thru
26927     }
26928     
26929 })/**
26930  * @class Roo.htmleditor.FilterLongBr
26931  * BR/BR/BR - keep a maximum of 2...
26932  * @constructor
26933  * Run a new Long BR Filter
26934  * @param {Object} config Configuration options
26935  */
26936
26937 Roo.htmleditor.FilterLongBr = function(cfg)
26938 {
26939     // no need to apply config.
26940     this.walk(cfg.node);
26941 }
26942
26943 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26944 {
26945     
26946      
26947     tag : 'BR',
26948     
26949      
26950     replaceTag : function(node)
26951     {
26952         
26953         var ps = node.nextSibling;
26954         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26955             ps = ps.nextSibling;
26956         }
26957         
26958         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26959             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26960             return false;
26961         }
26962         
26963         if (!ps || ps.nodeType != 1) {
26964             return false;
26965         }
26966         
26967         if (!ps || ps.tagName != 'BR') {
26968            
26969             return false;
26970         }
26971         
26972         
26973         
26974         
26975         
26976         if (!node.previousSibling) {
26977             return false;
26978         }
26979         var ps = node.previousSibling;
26980         
26981         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26982             ps = ps.previousSibling;
26983         }
26984         if (!ps || ps.nodeType != 1) {
26985             return false;
26986         }
26987         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26988         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26989             return false;
26990         }
26991         
26992         node.parentNode.removeChild(node); // remove me...
26993         
26994         return false; // no need to do children
26995
26996     }
26997     
26998 }); 
26999
27000 /**
27001  * @class Roo.htmleditor.FilterBlock
27002  * removes id / data-block and contenteditable that are associated with blocks
27003  * usage should be done on a cloned copy of the dom
27004  * @constructor
27005 * Run a new Attribute Filter { node : xxxx }}
27006 * @param {Object} config Configuration options
27007  */
27008 Roo.htmleditor.FilterBlock = function(cfg)
27009 {
27010     Roo.apply(this, cfg);
27011     var qa = cfg.node.querySelectorAll;
27012     this.removeAttributes('data-block');
27013     this.removeAttributes('contenteditable');
27014     this.removeAttributes('id');
27015     
27016 }
27017
27018 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27019 {
27020     node: true, // all tags
27021      
27022      
27023     removeAttributes : function(attr)
27024     {
27025         var ar = this.node.querySelectorAll('*[' + attr + ']');
27026         for (var i =0;i<ar.length;i++) {
27027             ar[i].removeAttribute(attr);
27028         }
27029     }
27030         
27031         
27032         
27033     
27034 });
27035 /**
27036  * @class Roo.htmleditor.KeyEnter
27037  * Handle Enter press..
27038  * @cfg {Roo.HtmlEditorCore} core the editor.
27039  * @constructor
27040  * Create a new Filter.
27041  * @param {Object} config Configuration options
27042  */
27043
27044
27045
27046
27047
27048 Roo.htmleditor.KeyEnter = function(cfg) {
27049     Roo.apply(this, cfg);
27050     // this does not actually call walk as it's really just a abstract class
27051  
27052     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
27053 }
27054
27055 //Roo.htmleditor.KeyEnter.i = 0;
27056
27057
27058 Roo.htmleditor.KeyEnter.prototype = {
27059     
27060     core : false,
27061     
27062     keypress : function(e)
27063     {
27064         if (e.charCode != 13 && e.charCode != 10) {
27065             Roo.log([e.charCode,e]);
27066             return true;
27067         }
27068         e.preventDefault();
27069         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
27070         var doc = this.core.doc;
27071           //add a new line
27072        
27073     
27074         var sel = this.core.getSelection();
27075         var range = sel.getRangeAt(0);
27076         var n = range.commonAncestorContainer;
27077         var pc = range.closest([ 'ol', 'ul']);
27078         var pli = range.closest('li');
27079         if (!pc || e.ctrlKey) {
27080             // on it list, or ctrl pressed.
27081             if (!e.ctrlKey) {
27082                 sel.insertNode('br', 'after'); 
27083             } else {
27084                 // only do this if we have ctrl key..
27085                 var br = doc.createElement('br');
27086                 br.className = 'clear';
27087                 br.setAttribute('style', 'clear: both');
27088                 sel.insertNode(br, 'after'); 
27089             }
27090             
27091          
27092             this.core.undoManager.addEvent();
27093             this.core.fireEditorEvent(e);
27094             return false;
27095         }
27096         
27097         // deal with <li> insetion
27098         if (pli.innerText.trim() == '' &&
27099             pli.previousSibling &&
27100             pli.previousSibling.nodeName == 'LI' &&
27101             pli.previousSibling.innerText.trim() ==  '') {
27102             pli.parentNode.removeChild(pli.previousSibling);
27103             sel.cursorAfter(pc);
27104             this.core.undoManager.addEvent();
27105             this.core.fireEditorEvent(e);
27106             return false;
27107         }
27108     
27109         var li = doc.createElement('LI');
27110         li.innerHTML = '&nbsp;';
27111         if (!pli || !pli.firstSibling) {
27112             pc.appendChild(li);
27113         } else {
27114             pli.parentNode.insertBefore(li, pli.firstSibling);
27115         }
27116         sel.cursorText (li.firstChild);
27117       
27118         this.core.undoManager.addEvent();
27119         this.core.fireEditorEvent(e);
27120
27121         return false;
27122         
27123     
27124         
27125         
27126          
27127     }
27128 };
27129      
27130 /**
27131  * @class Roo.htmleditor.Block
27132  * Base class for html editor blocks - do not use it directly .. extend it..
27133  * @cfg {DomElement} node The node to apply stuff to.
27134  * @cfg {String} friendly_name the name that appears in the context bar about this block
27135  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
27136  
27137  * @constructor
27138  * Create a new Filter.
27139  * @param {Object} config Configuration options
27140  */
27141
27142 Roo.htmleditor.Block  = function(cfg)
27143 {
27144     // do nothing .. should not be called really.
27145 }
27146 /**
27147  * factory method to get the block from an element (using cache if necessary)
27148  * @static
27149  * @param {HtmlElement} the dom element
27150  */
27151 Roo.htmleditor.Block.factory = function(node)
27152 {
27153     var cc = Roo.htmleditor.Block.cache;
27154     var id = Roo.get(node).id;
27155     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
27156         Roo.htmleditor.Block.cache[id].readElement(node);
27157         return Roo.htmleditor.Block.cache[id];
27158     }
27159     var db  = node.getAttribute('data-block');
27160     if (!db) {
27161         db = node.nodeName.toLowerCase().toUpperCaseFirst();
27162     }
27163     var cls = Roo.htmleditor['Block' + db];
27164     if (typeof(cls) == 'undefined') {
27165         //Roo.log(node.getAttribute('data-block'));
27166         Roo.log("OOps missing block : " + 'Block' + db);
27167         return false;
27168     }
27169     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27170     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
27171 };
27172
27173 /**
27174  * initalize all Elements from content that are 'blockable'
27175  * @static
27176  * @param the body element
27177  */
27178 Roo.htmleditor.Block.initAll = function(body, type)
27179 {
27180     if (typeof(type) == 'undefined') {
27181         var ia = Roo.htmleditor.Block.initAll;
27182         ia(body,'table');
27183         ia(body,'td');
27184         ia(body,'figure');
27185         return;
27186     }
27187     Roo.each(Roo.get(body).query(type), function(e) {
27188         Roo.htmleditor.Block.factory(e);    
27189     },this);
27190 };
27191 // question goes here... do we need to clear out this cache sometimes?
27192 // or show we make it relivant to the htmleditor.
27193 Roo.htmleditor.Block.cache = {};
27194
27195 Roo.htmleditor.Block.prototype = {
27196     
27197     node : false,
27198     
27199      // used by context menu
27200     friendly_name : 'Based Block',
27201     
27202     // text for button to delete this element
27203     deleteTitle : false,
27204     
27205     context : false,
27206     /**
27207      * Update a node with values from this object
27208      * @param {DomElement} node
27209      */
27210     updateElement : function(node)
27211     {
27212         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27213     },
27214      /**
27215      * convert to plain HTML for calling insertAtCursor..
27216      */
27217     toHTML : function()
27218     {
27219         return Roo.DomHelper.markup(this.toObject());
27220     },
27221     /**
27222      * used by readEleemnt to extract data from a node
27223      * may need improving as it's pretty basic
27224      
27225      * @param {DomElement} node
27226      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27227      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27228      * @param {String} style the style property - eg. text-align
27229      */
27230     getVal : function(node, tag, attr, style)
27231     {
27232         var n = node;
27233         if (tag !== true && n.tagName != tag.toUpperCase()) {
27234             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27235             // but kiss for now.
27236             n = node.getElementsByTagName(tag).item(0);
27237         }
27238         if (!n) {
27239             return '';
27240         }
27241         if (attr === false) {
27242             return n;
27243         }
27244         if (attr == 'html') {
27245             return n.innerHTML;
27246         }
27247         if (attr == 'style') {
27248             return n.style[style]; 
27249         }
27250         
27251         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27252             
27253     },
27254     /**
27255      * create a DomHelper friendly object - for use with 
27256      * Roo.DomHelper.markup / overwrite / etc..
27257      * (override this)
27258      */
27259     toObject : function()
27260     {
27261         return {};
27262     },
27263       /**
27264      * Read a node that has a 'data-block' property - and extract the values from it.
27265      * @param {DomElement} node - the node
27266      */
27267     readElement : function(node)
27268     {
27269         
27270     } 
27271     
27272     
27273 };
27274
27275  
27276
27277 /**
27278  * @class Roo.htmleditor.BlockFigure
27279  * Block that has an image and a figcaption
27280  * @cfg {String} image_src the url for the image
27281  * @cfg {String} align (left|right) alignment for the block default left
27282  * @cfg {String} caption the text to appear below  (and in the alt tag)
27283  * @cfg {String} caption_display (block|none) display or not the caption
27284  * @cfg {String|number} image_width the width of the image number or %?
27285  * @cfg {String|number} image_height the height of the image number or %?
27286  * 
27287  * @constructor
27288  * Create a new Filter.
27289  * @param {Object} config Configuration options
27290  */
27291
27292 Roo.htmleditor.BlockFigure = function(cfg)
27293 {
27294     if (cfg.node) {
27295         this.readElement(cfg.node);
27296         this.updateElement(cfg.node);
27297     }
27298     Roo.apply(this, cfg);
27299 }
27300 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27301  
27302     
27303     // setable values.
27304     image_src: '',
27305     align: 'center',
27306     caption : '',
27307     caption_display : 'block',
27308     width : '100%',
27309     cls : '',
27310     href: '',
27311     video_url : '',
27312     
27313     // margin: '2%', not used
27314     
27315     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
27316
27317     
27318     // used by context menu
27319     friendly_name : 'Image with caption',
27320     deleteTitle : "Delete Image and Caption",
27321     
27322     contextMenu : function(toolbar)
27323     {
27324         
27325         var block = function() {
27326             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27327         };
27328         
27329         
27330         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27331         
27332         var syncValue = toolbar.editorcore.syncValue;
27333         
27334         var fields = {};
27335         
27336         return [
27337              {
27338                 xtype : 'TextItem',
27339                 text : "Source: ",
27340                 xns : rooui.Toolbar  //Boostrap?
27341             },
27342             {
27343                 xtype : 'Button',
27344                 text: 'Change Image URL',
27345                  
27346                 listeners : {
27347                     click: function (btn, state)
27348                     {
27349                         var b = block();
27350                         
27351                         Roo.MessageBox.show({
27352                             title : "Image Source URL",
27353                             msg : "Enter the url for the image",
27354                             buttons: Roo.MessageBox.OKCANCEL,
27355                             fn: function(btn, val){
27356                                 if (btn != 'ok') {
27357                                     return;
27358                                 }
27359                                 b.image_src = val;
27360                                 b.updateElement();
27361                                 syncValue();
27362                                 toolbar.editorcore.onEditorEvent();
27363                             },
27364                             minWidth:250,
27365                             prompt:true,
27366                             //multiline: multiline,
27367                             modal : true,
27368                             value : b.image_src
27369                         });
27370                     }
27371                 },
27372                 xns : rooui.Toolbar
27373             },
27374          
27375             {
27376                 xtype : 'Button',
27377                 text: 'Change Link URL',
27378                  
27379                 listeners : {
27380                     click: function (btn, state)
27381                     {
27382                         var b = block();
27383                         
27384                         Roo.MessageBox.show({
27385                             title : "Link URL",
27386                             msg : "Enter the url for the link - leave blank to have no link",
27387                             buttons: Roo.MessageBox.OKCANCEL,
27388                             fn: function(btn, val){
27389                                 if (btn != 'ok') {
27390                                     return;
27391                                 }
27392                                 b.href = val;
27393                                 b.updateElement();
27394                                 syncValue();
27395                                 toolbar.editorcore.onEditorEvent();
27396                             },
27397                             minWidth:250,
27398                             prompt:true,
27399                             //multiline: multiline,
27400                             modal : true,
27401                             value : b.href
27402                         });
27403                     }
27404                 },
27405                 xns : rooui.Toolbar
27406             },
27407             {
27408                 xtype : 'Button',
27409                 text: 'Show Video URL',
27410                  
27411                 listeners : {
27412                     click: function (btn, state)
27413                     {
27414                         Roo.MessageBox.alert("Video URL",
27415                             block().video_url == '' ? 'This image is not linked ot a video' :
27416                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27417                     }
27418                 },
27419                 xns : rooui.Toolbar
27420             },
27421             
27422             
27423             {
27424                 xtype : 'TextItem',
27425                 text : "Width: ",
27426                 xns : rooui.Toolbar  //Boostrap?
27427             },
27428             {
27429                 xtype : 'ComboBox',
27430                 allowBlank : false,
27431                 displayField : 'val',
27432                 editable : true,
27433                 listWidth : 100,
27434                 triggerAction : 'all',
27435                 typeAhead : true,
27436                 valueField : 'val',
27437                 width : 70,
27438                 name : 'width',
27439                 listeners : {
27440                     select : function (combo, r, index)
27441                     {
27442                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27443                         var b = block();
27444                         b.width = r.get('val');
27445                         b.updateElement();
27446                         syncValue();
27447                         toolbar.editorcore.onEditorEvent();
27448                     }
27449                 },
27450                 xns : rooui.form,
27451                 store : {
27452                     xtype : 'SimpleStore',
27453                     data : [
27454                         ['100%'],
27455                         ['80%'],
27456                         ['50%'],
27457                         ['20%'],
27458                         ['10%']
27459                     ],
27460                     fields : [ 'val'],
27461                     xns : Roo.data
27462                 }
27463             },
27464             {
27465                 xtype : 'TextItem',
27466                 text : "Align: ",
27467                 xns : rooui.Toolbar  //Boostrap?
27468             },
27469             {
27470                 xtype : 'ComboBox',
27471                 allowBlank : false,
27472                 displayField : 'val',
27473                 editable : true,
27474                 listWidth : 100,
27475                 triggerAction : 'all',
27476                 typeAhead : true,
27477                 valueField : 'val',
27478                 width : 70,
27479                 name : 'align',
27480                 listeners : {
27481                     select : function (combo, r, index)
27482                     {
27483                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27484                         var b = block();
27485                         b.align = r.get('val');
27486                         b.updateElement();
27487                         syncValue();
27488                         toolbar.editorcore.onEditorEvent();
27489                     }
27490                 },
27491                 xns : rooui.form,
27492                 store : {
27493                     xtype : 'SimpleStore',
27494                     data : [
27495                         ['left'],
27496                         ['right'],
27497                         ['center']
27498                     ],
27499                     fields : [ 'val'],
27500                     xns : Roo.data
27501                 }
27502             },
27503             
27504             
27505             {
27506                 xtype : 'Button',
27507                 text: 'Hide Caption',
27508                 name : 'caption_display',
27509                 pressed : false,
27510                 enableToggle : true,
27511                 setValue : function(v) {
27512                     // this trigger toggle.
27513                      
27514                     this.setText(v ? "Hide Caption" : "Show Caption");
27515                     this.setPressed(v != 'block');
27516                 },
27517                 listeners : {
27518                     toggle: function (btn, state)
27519                     {
27520                         var b  = block();
27521                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27522                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27523                         b.updateElement();
27524                         syncValue();
27525                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27526                         toolbar.editorcore.onEditorEvent();
27527                     }
27528                 },
27529                 xns : rooui.Toolbar
27530             }
27531         ];
27532         
27533     },
27534     /**
27535      * create a DomHelper friendly object - for use with
27536      * Roo.DomHelper.markup / overwrite / etc..
27537      */
27538     toObject : function()
27539     {
27540         var d = document.createElement('div');
27541         d.innerHTML = this.caption;
27542         
27543         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
27544         
27545         var iw = this.align == 'center' ? this.width : '100%';
27546         var img =   {
27547             tag : 'img',
27548             contenteditable : 'false',
27549             src : this.image_src,
27550             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27551             style: {
27552                 width : iw,
27553                 maxWidth : iw + ' !important', // this is not getting rendered?
27554                 margin : m  
27555                 
27556             }
27557         };
27558         /*
27559         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27560                     '<a href="{2}">' + 
27561                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
27562                     '</a>' + 
27563                 '</div>',
27564         */
27565                 
27566         if (this.href.length > 0) {
27567             img = {
27568                 tag : 'a',
27569                 href: this.href,
27570                 contenteditable : 'true',
27571                 cn : [
27572                     img
27573                 ]
27574             };
27575         }
27576         
27577         
27578         if (this.video_url.length > 0) {
27579             img = {
27580                 tag : 'div',
27581                 cls : this.cls,
27582                 frameborder : 0,
27583                 allowfullscreen : true,
27584                 width : 420,  // these are for video tricks - that we replace the outer
27585                 height : 315,
27586                 src : this.video_url,
27587                 cn : [
27588                     img
27589                 ]
27590             };
27591         }
27592         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27593         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27594         
27595   
27596         var ret =   {
27597             tag: 'figure',
27598             'data-block' : 'Figure',
27599             'data-width' : this.width, 
27600             contenteditable : 'false',
27601             
27602             style : {
27603                 display: 'block',
27604                 float :  this.align ,
27605                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27606                 width : this.align == 'center' ? '100%' : this.width,
27607                 margin:  '0px',
27608                 padding: this.align == 'center' ? '0' : '0 10px' ,
27609                 textAlign : this.align   // seems to work for email..
27610                 
27611             },
27612            
27613             
27614             align : this.align,
27615             cn : [
27616                 img,
27617               
27618                 {
27619                     tag: 'figcaption',
27620                     'data-display' : this.caption_display,
27621                     style : {
27622                         textAlign : 'left',
27623                         fontSize : '16px',
27624                         lineHeight : '24px',
27625                         display : this.caption_display,
27626                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
27627                         margin: m,
27628                         width: this.align == 'center' ?  this.width : '100%' 
27629                     
27630                          
27631                     },
27632                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
27633                     cn : [
27634                         {
27635                             tag: 'div',
27636                             style  : {
27637                                 marginTop : '16px',
27638                                 textAlign : 'left'
27639                             },
27640                             align: 'left',
27641                             cn : [
27642                                 {
27643                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
27644                                     tag : 'i',
27645                                     contenteditable : true,
27646                                     html : captionhtml
27647                                 }
27648                                 
27649                             ]
27650                         }
27651                         
27652                     ]
27653                     
27654                 }
27655             ]
27656         };
27657         return ret;
27658          
27659     },
27660     
27661     readElement : function(node)
27662     {
27663         // this should not really come from the link...
27664         this.video_url = this.getVal(node, 'div', 'src');
27665         this.cls = this.getVal(node, 'div', 'class');
27666         this.href = this.getVal(node, 'a', 'href');
27667         
27668         
27669         this.image_src = this.getVal(node, 'img', 'src');
27670          
27671         this.align = this.getVal(node, 'figure', 'align');
27672         var figcaption = this.getVal(node, 'figcaption', false);
27673         if (figcaption !== '') {
27674             this.caption = this.getVal(figcaption, 'i', 'html');
27675         }
27676         
27677
27678         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27679         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27680         this.width = this.getVal(node, true, 'data-width');
27681         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27682         
27683     },
27684     removeNode : function()
27685     {
27686         return this.node;
27687     }
27688     
27689   
27690    
27691      
27692     
27693     
27694     
27695     
27696 })
27697
27698  
27699
27700 /**
27701  * @class Roo.htmleditor.BlockTable
27702  * Block that manages a table
27703  * 
27704  * @constructor
27705  * Create a new Filter.
27706  * @param {Object} config Configuration options
27707  */
27708
27709 Roo.htmleditor.BlockTable = function(cfg)
27710 {
27711     if (cfg.node) {
27712         this.readElement(cfg.node);
27713         this.updateElement(cfg.node);
27714     }
27715     Roo.apply(this, cfg);
27716     if (!cfg.node) {
27717         this.rows = [];
27718         for(var r = 0; r < this.no_row; r++) {
27719             this.rows[r] = [];
27720             for(var c = 0; c < this.no_col; c++) {
27721                 this.rows[r][c] = this.emptyCell();
27722             }
27723         }
27724     }
27725     
27726     
27727 }
27728 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27729  
27730     rows : false,
27731     no_col : 1,
27732     no_row : 1,
27733     
27734     
27735     width: '100%',
27736     
27737     // used by context menu
27738     friendly_name : 'Table',
27739     deleteTitle : 'Delete Table',
27740     // context menu is drawn once..
27741     
27742     contextMenu : function(toolbar)
27743     {
27744         
27745         var block = function() {
27746             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27747         };
27748         
27749         
27750         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27751         
27752         var syncValue = toolbar.editorcore.syncValue;
27753         
27754         var fields = {};
27755         
27756         return [
27757             {
27758                 xtype : 'TextItem',
27759                 text : "Width: ",
27760                 xns : rooui.Toolbar  //Boostrap?
27761             },
27762             {
27763                 xtype : 'ComboBox',
27764                 allowBlank : false,
27765                 displayField : 'val',
27766                 editable : true,
27767                 listWidth : 100,
27768                 triggerAction : 'all',
27769                 typeAhead : true,
27770                 valueField : 'val',
27771                 width : 100,
27772                 name : 'width',
27773                 listeners : {
27774                     select : function (combo, r, index)
27775                     {
27776                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27777                         var b = block();
27778                         b.width = r.get('val');
27779                         b.updateElement();
27780                         syncValue();
27781                         toolbar.editorcore.onEditorEvent();
27782                     }
27783                 },
27784                 xns : rooui.form,
27785                 store : {
27786                     xtype : 'SimpleStore',
27787                     data : [
27788                         ['100%'],
27789                         ['auto']
27790                     ],
27791                     fields : [ 'val'],
27792                     xns : Roo.data
27793                 }
27794             },
27795             // -------- Cols
27796             
27797             {
27798                 xtype : 'TextItem',
27799                 text : "Columns: ",
27800                 xns : rooui.Toolbar  //Boostrap?
27801             },
27802          
27803             {
27804                 xtype : 'Button',
27805                 text: '-',
27806                 listeners : {
27807                     click : function (_self, e)
27808                     {
27809                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27810                         block().removeColumn();
27811                         syncValue();
27812                         toolbar.editorcore.onEditorEvent();
27813                     }
27814                 },
27815                 xns : rooui.Toolbar
27816             },
27817             {
27818                 xtype : 'Button',
27819                 text: '+',
27820                 listeners : {
27821                     click : function (_self, e)
27822                     {
27823                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27824                         block().addColumn();
27825                         syncValue();
27826                         toolbar.editorcore.onEditorEvent();
27827                     }
27828                 },
27829                 xns : rooui.Toolbar
27830             },
27831             // -------- ROWS
27832             {
27833                 xtype : 'TextItem',
27834                 text : "Rows: ",
27835                 xns : rooui.Toolbar  //Boostrap?
27836             },
27837          
27838             {
27839                 xtype : 'Button',
27840                 text: '-',
27841                 listeners : {
27842                     click : function (_self, e)
27843                     {
27844                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27845                         block().removeRow();
27846                         syncValue();
27847                         toolbar.editorcore.onEditorEvent();
27848                     }
27849                 },
27850                 xns : rooui.Toolbar
27851             },
27852             {
27853                 xtype : 'Button',
27854                 text: '+',
27855                 listeners : {
27856                     click : function (_self, e)
27857                     {
27858                         block().addRow();
27859                         syncValue();
27860                         toolbar.editorcore.onEditorEvent();
27861                     }
27862                 },
27863                 xns : rooui.Toolbar
27864             },
27865             // -------- ROWS
27866             {
27867                 xtype : 'Button',
27868                 text: 'Reset Column Widths',
27869                 listeners : {
27870                     
27871                     click : function (_self, e)
27872                     {
27873                         block().resetWidths();
27874                         syncValue();
27875                         toolbar.editorcore.onEditorEvent();
27876                     }
27877                 },
27878                 xns : rooui.Toolbar
27879             } 
27880             
27881             
27882             
27883         ];
27884         
27885     },
27886     
27887     
27888   /**
27889      * create a DomHelper friendly object - for use with
27890      * Roo.DomHelper.markup / overwrite / etc..
27891      * ?? should it be called with option to hide all editing features?
27892      */
27893     toObject : function()
27894     {
27895         
27896         var ret = {
27897             tag : 'table',
27898             contenteditable : 'false', // this stops cell selection from picking the table.
27899             'data-block' : 'Table',
27900             style : {
27901                 width:  this.width,
27902                 border : 'solid 1px #000', // ??? hard coded?
27903                 'border-collapse' : 'collapse' 
27904             },
27905             cn : [
27906                 { tag : 'tbody' , cn : [] }
27907             ]
27908         };
27909         
27910         // do we have a head = not really 
27911         var ncols = 0;
27912         Roo.each(this.rows, function( row ) {
27913             var tr = {
27914                 tag: 'tr',
27915                 style : {
27916                     margin: '6px',
27917                     border : 'solid 1px #000',
27918                     textAlign : 'left' 
27919                 },
27920                 cn : [ ]
27921             };
27922             
27923             ret.cn[0].cn.push(tr);
27924             // does the row have any properties? ?? height?
27925             var nc = 0;
27926             Roo.each(row, function( cell ) {
27927                 
27928                 var td = {
27929                     tag : 'td',
27930                     contenteditable :  'true',
27931                     'data-block' : 'Td',
27932                     html : cell.html,
27933                     style : cell.style
27934                 };
27935                 if (cell.colspan > 1) {
27936                     td.colspan = cell.colspan ;
27937                     nc += cell.colspan;
27938                 } else {
27939                     nc++;
27940                 }
27941                 if (cell.rowspan > 1) {
27942                     td.rowspan = cell.rowspan ;
27943                 }
27944                 
27945                 
27946                 // widths ?
27947                 tr.cn.push(td);
27948                     
27949                 
27950             }, this);
27951             ncols = Math.max(nc, ncols);
27952             
27953             
27954         }, this);
27955         // add the header row..
27956         
27957         ncols++;
27958          
27959         
27960         return ret;
27961          
27962     },
27963     
27964     readElement : function(node)
27965     {
27966         node  = node ? node : this.node ;
27967         this.width = this.getVal(node, true, 'style', 'width') || '100%';
27968         
27969         this.rows = [];
27970         this.no_row = 0;
27971         var trs = Array.from(node.rows);
27972         trs.forEach(function(tr) {
27973             var row =  [];
27974             this.rows.push(row);
27975             
27976             this.no_row++;
27977             var no_column = 0;
27978             Array.from(tr.cells).forEach(function(td) {
27979                 
27980                 var add = {
27981                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27982                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27983                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27984                     html : td.innerHTML
27985                 };
27986                 no_column += add.colspan;
27987                      
27988                 
27989                 row.push(add);
27990                 
27991                 
27992             },this);
27993             this.no_col = Math.max(this.no_col, no_column);
27994             
27995             
27996         },this);
27997         
27998         
27999     },
28000     normalizeRows: function()
28001     {
28002         var ret= [];
28003         var rid = -1;
28004         this.rows.forEach(function(row) {
28005             rid++;
28006             ret[rid] = [];
28007             row = this.normalizeRow(row);
28008             var cid = 0;
28009             row.forEach(function(c) {
28010                 while (typeof(ret[rid][cid]) != 'undefined') {
28011                     cid++;
28012                 }
28013                 if (typeof(ret[rid]) == 'undefined') {
28014                     ret[rid] = [];
28015                 }
28016                 ret[rid][cid] = c;
28017                 c.row = rid;
28018                 c.col = cid;
28019                 if (c.rowspan < 2) {
28020                     return;
28021                 }
28022                 
28023                 for(var i = 1 ;i < c.rowspan; i++) {
28024                     if (typeof(ret[rid+i]) == 'undefined') {
28025                         ret[rid+i] = [];
28026                     }
28027                     ret[rid+i][cid] = c;
28028                 }
28029             });
28030         }, this);
28031         return ret;
28032     
28033     },
28034     
28035     normalizeRow: function(row)
28036     {
28037         var ret= [];
28038         row.forEach(function(c) {
28039             if (c.colspan < 2) {
28040                 ret.push(c);
28041                 return;
28042             }
28043             for(var i =0 ;i < c.colspan; i++) {
28044                 ret.push(c);
28045             }
28046         });
28047         return ret;
28048     
28049     },
28050     
28051     deleteColumn : function(sel)
28052     {
28053         if (!sel || sel.type != 'col') {
28054             return;
28055         }
28056         if (this.no_col < 2) {
28057             return;
28058         }
28059         
28060         this.rows.forEach(function(row) {
28061             var cols = this.normalizeRow(row);
28062             var col = cols[sel.col];
28063             if (col.colspan > 1) {
28064                 col.colspan --;
28065             } else {
28066                 row.remove(col);
28067             }
28068             
28069         }, this);
28070         this.no_col--;
28071         
28072     },
28073     removeColumn : function()
28074     {
28075         this.deleteColumn({
28076             type: 'col',
28077             col : this.no_col-1
28078         });
28079         this.updateElement();
28080     },
28081     
28082      
28083     addColumn : function()
28084     {
28085         
28086         this.rows.forEach(function(row) {
28087             row.push(this.emptyCell());
28088            
28089         }, this);
28090         this.updateElement();
28091     },
28092     
28093     deleteRow : function(sel)
28094     {
28095         if (!sel || sel.type != 'row') {
28096             return;
28097         }
28098         
28099         if (this.no_row < 2) {
28100             return;
28101         }
28102         
28103         var rows = this.normalizeRows();
28104         
28105         
28106         rows[sel.row].forEach(function(col) {
28107             if (col.rowspan > 1) {
28108                 col.rowspan--;
28109             } else {
28110                 col.remove = 1; // flage it as removed.
28111             }
28112             
28113         }, this);
28114         var newrows = [];
28115         this.rows.forEach(function(row) {
28116             newrow = [];
28117             row.forEach(function(c) {
28118                 if (typeof(c.remove) == 'undefined') {
28119                     newrow.push(c);
28120                 }
28121                 
28122             });
28123             if (newrow.length > 0) {
28124                 newrows.push(row);
28125             }
28126         });
28127         this.rows =  newrows;
28128         
28129         
28130         
28131         this.no_row--;
28132         this.updateElement();
28133         
28134     },
28135     removeRow : function()
28136     {
28137         this.deleteRow({
28138             type: 'row',
28139             row : this.no_row-1
28140         });
28141         
28142     },
28143     
28144      
28145     addRow : function()
28146     {
28147         
28148         var row = [];
28149         for (var i = 0; i < this.no_col; i++ ) {
28150             
28151             row.push(this.emptyCell());
28152            
28153         }
28154         this.rows.push(row);
28155         this.updateElement();
28156         
28157     },
28158      
28159     // the default cell object... at present...
28160     emptyCell : function() {
28161         return (new Roo.htmleditor.BlockTd({})).toObject();
28162         
28163      
28164     },
28165     
28166     removeNode : function()
28167     {
28168         return this.node;
28169     },
28170     
28171     
28172     
28173     resetWidths : function()
28174     {
28175         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28176             var nn = Roo.htmleditor.Block.factory(n);
28177             nn.width = '';
28178             nn.updateElement(n);
28179         });
28180     }
28181     
28182     
28183     
28184     
28185 })
28186
28187 /**
28188  *
28189  * editing a TD?
28190  *
28191  * since selections really work on the table cell, then editing really should work from there
28192  *
28193  * The original plan was to support merging etc... - but that may not be needed yet..
28194  *
28195  * So this simple version will support:
28196  *   add/remove cols
28197  *   adjust the width +/-
28198  *   reset the width...
28199  *   
28200  *
28201  */
28202
28203
28204  
28205
28206 /**
28207  * @class Roo.htmleditor.BlockTable
28208  * Block that manages a table
28209  * 
28210  * @constructor
28211  * Create a new Filter.
28212  * @param {Object} config Configuration options
28213  */
28214
28215 Roo.htmleditor.BlockTd = function(cfg)
28216 {
28217     if (cfg.node) {
28218         this.readElement(cfg.node);
28219         this.updateElement(cfg.node);
28220     }
28221     Roo.apply(this, cfg);
28222      
28223     
28224     
28225 }
28226 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28227  
28228     node : false,
28229     
28230     width: '',
28231     textAlign : 'left',
28232     valign : 'top',
28233     
28234     colspan : 1,
28235     rowspan : 1,
28236     
28237     
28238     // used by context menu
28239     friendly_name : 'Table Cell',
28240     deleteTitle : false, // use our customer delete
28241     
28242     // context menu is drawn once..
28243     
28244     contextMenu : function(toolbar)
28245     {
28246         
28247         var cell = function() {
28248             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28249         };
28250         
28251         var table = function() {
28252             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28253         };
28254         
28255         var lr = false;
28256         var saveSel = function()
28257         {
28258             lr = toolbar.editorcore.getSelection().getRangeAt(0);
28259         }
28260         var restoreSel = function()
28261         {
28262             if (lr) {
28263                 (function() {
28264                     toolbar.editorcore.focus();
28265                     var cr = toolbar.editorcore.getSelection();
28266                     cr.removeAllRanges();
28267                     cr.addRange(lr);
28268                     toolbar.editorcore.onEditorEvent();
28269                 }).defer(10, this);
28270                 
28271                 
28272             }
28273         }
28274         
28275         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28276         
28277         var syncValue = toolbar.editorcore.syncValue;
28278         
28279         var fields = {};
28280         
28281         return [
28282             {
28283                 xtype : 'Button',
28284                 text : 'Edit Table',
28285                 listeners : {
28286                     click : function() {
28287                         var t = toolbar.tb.selectedNode.closest('table');
28288                         toolbar.editorcore.selectNode(t);
28289                         toolbar.editorcore.onEditorEvent();                        
28290                     }
28291                 }
28292                 
28293             },
28294               
28295            
28296              
28297             {
28298                 xtype : 'TextItem',
28299                 text : "Column Width: ",
28300                  xns : rooui.Toolbar 
28301                
28302             },
28303             {
28304                 xtype : 'Button',
28305                 text: '-',
28306                 listeners : {
28307                     click : function (_self, e)
28308                     {
28309                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28310                         cell().shrinkColumn();
28311                         syncValue();
28312                          toolbar.editorcore.onEditorEvent();
28313                     }
28314                 },
28315                 xns : rooui.Toolbar
28316             },
28317             {
28318                 xtype : 'Button',
28319                 text: '+',
28320                 listeners : {
28321                     click : function (_self, e)
28322                     {
28323                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28324                         cell().growColumn();
28325                         syncValue();
28326                         toolbar.editorcore.onEditorEvent();
28327                     }
28328                 },
28329                 xns : rooui.Toolbar
28330             },
28331             
28332             {
28333                 xtype : 'TextItem',
28334                 text : "Vertical Align: ",
28335                 xns : rooui.Toolbar  //Boostrap?
28336             },
28337             {
28338                 xtype : 'ComboBox',
28339                 allowBlank : false,
28340                 displayField : 'val',
28341                 editable : true,
28342                 listWidth : 100,
28343                 triggerAction : 'all',
28344                 typeAhead : true,
28345                 valueField : 'val',
28346                 width : 100,
28347                 name : 'valign',
28348                 listeners : {
28349                     select : function (combo, r, index)
28350                     {
28351                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28352                         var b = cell();
28353                         b.valign = r.get('val');
28354                         b.updateElement();
28355                         syncValue();
28356                         toolbar.editorcore.onEditorEvent();
28357                     }
28358                 },
28359                 xns : rooui.form,
28360                 store : {
28361                     xtype : 'SimpleStore',
28362                     data : [
28363                         ['top'],
28364                         ['middle'],
28365                         ['bottom'] // there are afew more... 
28366                     ],
28367                     fields : [ 'val'],
28368                     xns : Roo.data
28369                 }
28370             },
28371             
28372             {
28373                 xtype : 'TextItem',
28374                 text : "Merge Cells: ",
28375                  xns : rooui.Toolbar 
28376                
28377             },
28378             
28379             
28380             {
28381                 xtype : 'Button',
28382                 text: 'Right',
28383                 listeners : {
28384                     click : function (_self, e)
28385                     {
28386                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28387                         cell().mergeRight();
28388                         //block().growColumn();
28389                         syncValue();
28390                         toolbar.editorcore.onEditorEvent();
28391                     }
28392                 },
28393                 xns : rooui.Toolbar
28394             },
28395              
28396             {
28397                 xtype : 'Button',
28398                 text: 'Below',
28399                 listeners : {
28400                     click : function (_self, e)
28401                     {
28402                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28403                         cell().mergeBelow();
28404                         //block().growColumn();
28405                         syncValue();
28406                         toolbar.editorcore.onEditorEvent();
28407                     }
28408                 },
28409                 xns : rooui.Toolbar
28410             },
28411             {
28412                 xtype : 'TextItem',
28413                 text : "| ",
28414                  xns : rooui.Toolbar 
28415                
28416             },
28417             
28418             {
28419                 xtype : 'Button',
28420                 text: 'Split',
28421                 listeners : {
28422                     click : function (_self, e)
28423                     {
28424                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28425                         cell().split();
28426                         syncValue();
28427                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28428                         toolbar.editorcore.onEditorEvent();
28429                                              
28430                     }
28431                 },
28432                 xns : rooui.Toolbar
28433             },
28434             {
28435                 xtype : 'Fill',
28436                 xns : rooui.Toolbar 
28437                
28438             },
28439         
28440           
28441             {
28442                 xtype : 'Button',
28443                 text: 'Delete',
28444                  
28445                 xns : rooui.Toolbar,
28446                 menu : {
28447                     xtype : 'Menu',
28448                     xns : rooui.menu,
28449                     items : [
28450                         {
28451                             xtype : 'Item',
28452                             html: 'Column',
28453                             listeners : {
28454                                 click : function (_self, e)
28455                                 {
28456                                     var t = table();
28457                                     
28458                                     cell().deleteColumn();
28459                                     syncValue();
28460                                     toolbar.editorcore.selectNode(t.node);
28461                                     toolbar.editorcore.onEditorEvent();   
28462                                 }
28463                             },
28464                             xns : rooui.menu
28465                         },
28466                         {
28467                             xtype : 'Item',
28468                             html: 'Row',
28469                             listeners : {
28470                                 click : function (_self, e)
28471                                 {
28472                                     var t = table();
28473                                     cell().deleteRow();
28474                                     syncValue();
28475                                     
28476                                     toolbar.editorcore.selectNode(t.node);
28477                                     toolbar.editorcore.onEditorEvent();   
28478                                                          
28479                                 }
28480                             },
28481                             xns : rooui.menu
28482                         },
28483                        {
28484                             xtype : 'Separator',
28485                             xns : rooui.menu
28486                         },
28487                         {
28488                             xtype : 'Item',
28489                             html: 'Table',
28490                             listeners : {
28491                                 click : function (_self, e)
28492                                 {
28493                                     var t = table();
28494                                     var nn = t.node.nextSibling || t.node.previousSibling;
28495                                     t.node.parentNode.removeChild(t.node);
28496                                     if (nn) { 
28497                                         toolbar.editorcore.selectNode(nn, true);
28498                                     }
28499                                     toolbar.editorcore.onEditorEvent();   
28500                                                          
28501                                 }
28502                             },
28503                             xns : rooui.menu
28504                         }
28505                     ]
28506                 }
28507             }
28508             
28509             // align... << fixme
28510             
28511         ];
28512         
28513     },
28514     
28515     
28516   /**
28517      * create a DomHelper friendly object - for use with
28518      * Roo.DomHelper.markup / overwrite / etc..
28519      * ?? should it be called with option to hide all editing features?
28520      */
28521  /**
28522      * create a DomHelper friendly object - for use with
28523      * Roo.DomHelper.markup / overwrite / etc..
28524      * ?? should it be called with option to hide all editing features?
28525      */
28526     toObject : function()
28527     {
28528         var ret = {
28529             tag : 'td',
28530             contenteditable : 'true', // this stops cell selection from picking the table.
28531             'data-block' : 'Td',
28532             valign : this.valign,
28533             style : {  
28534                 'text-align' :  this.textAlign,
28535                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28536                 'border-collapse' : 'collapse',
28537                 padding : '6px', // 8 for desktop / 4 for mobile
28538                 'vertical-align': this.valign
28539             },
28540             html : this.html
28541         };
28542         if (this.width != '') {
28543             ret.width = this.width;
28544             ret.style.width = this.width;
28545         }
28546         
28547         
28548         if (this.colspan > 1) {
28549             ret.colspan = this.colspan ;
28550         } 
28551         if (this.rowspan > 1) {
28552             ret.rowspan = this.rowspan ;
28553         }
28554         
28555            
28556         
28557         return ret;
28558          
28559     },
28560     
28561     readElement : function(node)
28562     {
28563         node  = node ? node : this.node ;
28564         this.width = node.style.width;
28565         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28566         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28567         this.html = node.innerHTML;
28568         if (node.style.textAlign != '') {
28569             this.textAlign = node.style.textAlign;
28570         }
28571         
28572         
28573     },
28574      
28575     // the default cell object... at present...
28576     emptyCell : function() {
28577         return {
28578             colspan :  1,
28579             rowspan :  1,
28580             textAlign : 'left',
28581             html : "&nbsp;" // is this going to be editable now?
28582         };
28583      
28584     },
28585     
28586     removeNode : function()
28587     {
28588         return this.node.closest('table');
28589          
28590     },
28591     
28592     cellData : false,
28593     
28594     colWidths : false,
28595     
28596     toTableArray  : function()
28597     {
28598         var ret = [];
28599         var tab = this.node.closest('tr').closest('table');
28600         Array.from(tab.rows).forEach(function(r, ri){
28601             ret[ri] = [];
28602         });
28603         var rn = 0;
28604         this.colWidths = [];
28605         var all_auto = true;
28606         Array.from(tab.rows).forEach(function(r, ri){
28607             
28608             var cn = 0;
28609             Array.from(r.cells).forEach(function(ce, ci){
28610                 var c =  {
28611                     cell : ce,
28612                     row : rn,
28613                     col: cn,
28614                     colspan : ce.colSpan,
28615                     rowspan : ce.rowSpan
28616                 };
28617                 if (ce.isEqualNode(this.node)) {
28618                     this.cellData = c;
28619                 }
28620                 // if we have been filled up by a row?
28621                 if (typeof(ret[rn][cn]) != 'undefined') {
28622                     while(typeof(ret[rn][cn]) != 'undefined') {
28623                         cn++;
28624                     }
28625                     c.col = cn;
28626                 }
28627                 
28628                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
28629                     this.colWidths[cn] =   ce.style.width;
28630                     if (this.colWidths[cn] != '') {
28631                         all_auto = false;
28632                     }
28633                 }
28634                 
28635                 
28636                 if (c.colspan < 2 && c.rowspan < 2 ) {
28637                     ret[rn][cn] = c;
28638                     cn++;
28639                     return;
28640                 }
28641                 for(var j = 0; j < c.rowspan; j++) {
28642                     if (typeof(ret[rn+j]) == 'undefined') {
28643                         continue; // we have a problem..
28644                     }
28645                     ret[rn+j][cn] = c;
28646                     for(var i = 0; i < c.colspan; i++) {
28647                         ret[rn+j][cn+i] = c;
28648                     }
28649                 }
28650                 
28651                 cn += c.colspan;
28652             }, this);
28653             rn++;
28654         }, this);
28655         
28656         // initalize widths.?
28657         // either all widths or no widths..
28658         if (all_auto) {
28659             this.colWidths[0] = false; // no widths flag.
28660         }
28661         
28662         
28663         return ret;
28664         
28665     },
28666     
28667     
28668     
28669     
28670     mergeRight: function()
28671     {
28672          
28673         // get the contents of the next cell along..
28674         var tr = this.node.closest('tr');
28675         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28676         if (i >= tr.childNodes.length - 1) {
28677             return; // no cells on right to merge with.
28678         }
28679         var table = this.toTableArray();
28680         
28681         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28682             return; // nothing right?
28683         }
28684         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28685         // right cell - must be same rowspan and on the same row.
28686         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28687             return; // right hand side is not same rowspan.
28688         }
28689         
28690         
28691         
28692         this.node.innerHTML += ' ' + rc.cell.innerHTML;
28693         tr.removeChild(rc.cell);
28694         this.colspan += rc.colspan;
28695         this.node.setAttribute('colspan', this.colspan);
28696
28697         var table = this.toTableArray();
28698         this.normalizeWidths(table);
28699         this.updateWidths(table);
28700     },
28701     
28702     
28703     mergeBelow : function()
28704     {
28705         var table = this.toTableArray();
28706         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28707             return; // no row below
28708         }
28709         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28710             return; // nothing right?
28711         }
28712         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28713         
28714         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28715             return; // right hand side is not same rowspan.
28716         }
28717         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
28718         rc.cell.parentNode.removeChild(rc.cell);
28719         this.rowspan += rc.rowspan;
28720         this.node.setAttribute('rowspan', this.rowspan);
28721     },
28722     
28723     split: function()
28724     {
28725         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28726             return;
28727         }
28728         var table = this.toTableArray();
28729         var cd = this.cellData;
28730         this.rowspan = 1;
28731         this.colspan = 1;
28732         
28733         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28734              
28735             
28736             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28737                 if (r == cd.row && c == cd.col) {
28738                     this.node.removeAttribute('rowspan');
28739                     this.node.removeAttribute('colspan');
28740                 }
28741                  
28742                 var ntd = this.node.cloneNode(); // which col/row should be 0..
28743                 ntd.removeAttribute('id'); 
28744                 ntd.style.width  = this.colWidths[c];
28745                 ntd.innerHTML = '';
28746                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
28747             }
28748             
28749         }
28750         this.redrawAllCells(table);
28751         
28752     },
28753     
28754     
28755     
28756     redrawAllCells: function(table)
28757     {
28758         
28759          
28760         var tab = this.node.closest('tr').closest('table');
28761         var ctr = tab.rows[0].parentNode;
28762         Array.from(tab.rows).forEach(function(r, ri){
28763             
28764             Array.from(r.cells).forEach(function(ce, ci){
28765                 ce.parentNode.removeChild(ce);
28766             });
28767             r.parentNode.removeChild(r);
28768         });
28769         for(var r = 0 ; r < table.length; r++) {
28770             var re = tab.rows[r];
28771             
28772             var re = tab.ownerDocument.createElement('tr');
28773             ctr.appendChild(re);
28774             for(var c = 0 ; c < table[r].length; c++) {
28775                 if (table[r][c].cell === false) {
28776                     continue;
28777                 }
28778                 
28779                 re.appendChild(table[r][c].cell);
28780                  
28781                 table[r][c].cell = false;
28782             }
28783         }
28784         
28785     },
28786     updateWidths : function(table)
28787     {
28788         for(var r = 0 ; r < table.length; r++) {
28789            
28790             for(var c = 0 ; c < table[r].length; c++) {
28791                 if (table[r][c].cell === false) {
28792                     continue;
28793                 }
28794                 
28795                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28796                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28797                     el.width = Math.floor(this.colWidths[c])  +'%';
28798                     el.updateElement(el.node);
28799                 }
28800                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
28801                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28802                     var width = 0;
28803                     for(var i = 0; i < table[r][c].colspan; i ++) {
28804                         width += Math.floor(this.colWidths[c + i]);
28805                     }
28806                     el.width = width  +'%';
28807                     el.updateElement(el.node);
28808                 }
28809                 table[r][c].cell = false; // done
28810             }
28811         }
28812     },
28813     normalizeWidths : function(table)
28814     {
28815         if (this.colWidths[0] === false) {
28816             var nw = 100.0 / this.colWidths.length;
28817             this.colWidths.forEach(function(w,i) {
28818                 this.colWidths[i] = nw;
28819             },this);
28820             return;
28821         }
28822     
28823         var t = 0, missing = [];
28824         
28825         this.colWidths.forEach(function(w,i) {
28826             //if you mix % and
28827             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28828             var add =  this.colWidths[i];
28829             if (add > 0) {
28830                 t+=add;
28831                 return;
28832             }
28833             missing.push(i);
28834             
28835             
28836         },this);
28837         var nc = this.colWidths.length;
28838         if (missing.length) {
28839             var mult = (nc - missing.length) / (1.0 * nc);
28840             var t = mult * t;
28841             var ew = (100 -t) / (1.0 * missing.length);
28842             this.colWidths.forEach(function(w,i) {
28843                 if (w > 0) {
28844                     this.colWidths[i] = w * mult;
28845                     return;
28846                 }
28847                 
28848                 this.colWidths[i] = ew;
28849             }, this);
28850             // have to make up numbers..
28851              
28852         }
28853         // now we should have all the widths..
28854         
28855     
28856     },
28857     
28858     shrinkColumn : function()
28859     {
28860         var table = this.toTableArray();
28861         this.normalizeWidths(table);
28862         var col = this.cellData.col;
28863         var nw = this.colWidths[col] * 0.8;
28864         if (nw < 5) {
28865             return;
28866         }
28867         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28868         this.colWidths.forEach(function(w,i) {
28869             if (i == col) {
28870                  this.colWidths[i] = nw;
28871                 return;
28872             }
28873             this.colWidths[i] += otherAdd
28874         }, this);
28875         this.updateWidths(table);
28876          
28877     },
28878     growColumn : function()
28879     {
28880         var table = this.toTableArray();
28881         this.normalizeWidths(table);
28882         var col = this.cellData.col;
28883         var nw = this.colWidths[col] * 1.2;
28884         if (nw > 90) {
28885             return;
28886         }
28887         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28888         this.colWidths.forEach(function(w,i) {
28889             if (i == col) {
28890                 this.colWidths[i] = nw;
28891                 return;
28892             }
28893             this.colWidths[i] -= otherSub
28894         }, this);
28895         this.updateWidths(table);
28896          
28897     },
28898     deleteRow : function()
28899     {
28900         // delete this rows 'tr'
28901         // if any of the cells in this row have a rowspan > 1 && row!= this row..
28902         // then reduce the rowspan.
28903         var table = this.toTableArray();
28904         // this.cellData.row;
28905         for (var i =0;i< table[this.cellData.row].length ; i++) {
28906             var c = table[this.cellData.row][i];
28907             if (c.row != this.cellData.row) {
28908                 
28909                 c.rowspan--;
28910                 c.cell.setAttribute('rowspan', c.rowspan);
28911                 continue;
28912             }
28913             if (c.rowspan > 1) {
28914                 c.rowspan--;
28915                 c.cell.setAttribute('rowspan', c.rowspan);
28916             }
28917         }
28918         table.splice(this.cellData.row,1);
28919         this.redrawAllCells(table);
28920         
28921     },
28922     deleteColumn : function()
28923     {
28924         var table = this.toTableArray();
28925         
28926         for (var i =0;i< table.length ; i++) {
28927             var c = table[i][this.cellData.col];
28928             if (c.col != this.cellData.col) {
28929                 table[i][this.cellData.col].colspan--;
28930             } else if (c.colspan > 1) {
28931                 c.colspan--;
28932                 c.cell.setAttribute('colspan', c.colspan);
28933             }
28934             table[i].splice(this.cellData.col,1);
28935         }
28936         
28937         this.redrawAllCells(table);
28938     }
28939     
28940     
28941     
28942     
28943 })
28944
28945 //<script type="text/javascript">
28946
28947 /*
28948  * Based  Ext JS Library 1.1.1
28949  * Copyright(c) 2006-2007, Ext JS, LLC.
28950  * LGPL
28951  *
28952  */
28953  
28954 /**
28955  * @class Roo.HtmlEditorCore
28956  * @extends Roo.Component
28957  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28958  *
28959  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28960  */
28961
28962 Roo.HtmlEditorCore = function(config){
28963     
28964     
28965     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28966     
28967     
28968     this.addEvents({
28969         /**
28970          * @event initialize
28971          * Fires when the editor is fully initialized (including the iframe)
28972          * @param {Roo.HtmlEditorCore} this
28973          */
28974         initialize: true,
28975         /**
28976          * @event activate
28977          * Fires when the editor is first receives the focus. Any insertion must wait
28978          * until after this event.
28979          * @param {Roo.HtmlEditorCore} this
28980          */
28981         activate: true,
28982          /**
28983          * @event beforesync
28984          * Fires before the textarea is updated with content from the editor iframe. Return false
28985          * to cancel the sync.
28986          * @param {Roo.HtmlEditorCore} this
28987          * @param {String} html
28988          */
28989         beforesync: true,
28990          /**
28991          * @event beforepush
28992          * Fires before the iframe editor is updated with content from the textarea. Return false
28993          * to cancel the push.
28994          * @param {Roo.HtmlEditorCore} this
28995          * @param {String} html
28996          */
28997         beforepush: true,
28998          /**
28999          * @event sync
29000          * Fires when the textarea is updated with content from the editor iframe.
29001          * @param {Roo.HtmlEditorCore} this
29002          * @param {String} html
29003          */
29004         sync: true,
29005          /**
29006          * @event push
29007          * Fires when the iframe editor is updated with content from the textarea.
29008          * @param {Roo.HtmlEditorCore} this
29009          * @param {String} html
29010          */
29011         push: true,
29012         
29013         /**
29014          * @event editorevent
29015          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
29016          * @param {Roo.HtmlEditorCore} this
29017          */
29018         editorevent: true 
29019          
29020         
29021     });
29022     
29023     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
29024     
29025     // defaults : white / black...
29026     this.applyBlacklists();
29027     
29028     
29029     
29030 };
29031
29032
29033 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
29034
29035
29036      /**
29037      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
29038      */
29039     
29040     owner : false,
29041     
29042      /**
29043      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
29044      *                        Roo.resizable.
29045      */
29046     resizable : false,
29047      /**
29048      * @cfg {Number} height (in pixels)
29049      */   
29050     height: 300,
29051    /**
29052      * @cfg {Number} width (in pixels)
29053      */   
29054     width: 500,
29055      /**
29056      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
29057      *         if you are doing an email editor, this probably needs disabling, it's designed
29058      */
29059     autoClean: true,
29060     
29061     /**
29062      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
29063      */
29064     enableBlocks : true,
29065     /**
29066      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
29067      * 
29068      */
29069     stylesheets: false,
29070      /**
29071      * @cfg {String} language default en - language of text (usefull for rtl languages)
29072      * 
29073      */
29074     language: 'en',
29075     
29076     /**
29077      * @cfg {boolean} allowComments - default false - allow comments in HTML source
29078      *          - by default they are stripped - if you are editing email you may need this.
29079      */
29080     allowComments: false,
29081     // id of frame..
29082     frameId: false,
29083     
29084     // private properties
29085     validationEvent : false,
29086     deferHeight: true,
29087     initialized : false,
29088     activated : false,
29089     sourceEditMode : false,
29090     onFocus : Roo.emptyFn,
29091     iframePad:3,
29092     hideMode:'offsets',
29093     
29094     clearUp: true,
29095     
29096     // blacklist + whitelisted elements..
29097     black: false,
29098     white: false,
29099      
29100     bodyCls : '',
29101
29102     
29103     undoManager : false,
29104     /**
29105      * Protected method that will not generally be called directly. It
29106      * is called when the editor initializes the iframe with HTML contents. Override this method if you
29107      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
29108      */
29109     getDocMarkup : function(){
29110         // body styles..
29111         var st = '';
29112         
29113         // inherit styels from page...?? 
29114         if (this.stylesheets === false) {
29115             
29116             Roo.get(document.head).select('style').each(function(node) {
29117                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29118             });
29119             
29120             Roo.get(document.head).select('link').each(function(node) { 
29121                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29122             });
29123             
29124         } else if (!this.stylesheets.length) {
29125                 // simple..
29126                 st = '<style type="text/css">' +
29127                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29128                    '</style>';
29129         } else {
29130             for (var i in this.stylesheets) {
29131                 if (typeof(this.stylesheets[i]) != 'string') {
29132                     continue;
29133                 }
29134                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
29135             }
29136             
29137         }
29138         
29139         st +=  '<style type="text/css">' +
29140             'IMG { cursor: pointer } ' +
29141         '</style>';
29142         
29143         st += '<meta name="google" content="notranslate">';
29144         
29145         var cls = 'notranslate roo-htmleditor-body';
29146         
29147         if(this.bodyCls.length){
29148             cls += ' ' + this.bodyCls;
29149         }
29150         
29151         return '<html  class="notranslate" translate="no"><head>' + st  +
29152             //<style type="text/css">' +
29153             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29154             //'</style>' +
29155             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
29156     },
29157
29158     // private
29159     onRender : function(ct, position)
29160     {
29161         var _t = this;
29162         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
29163         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
29164         
29165         
29166         this.el.dom.style.border = '0 none';
29167         this.el.dom.setAttribute('tabIndex', -1);
29168         this.el.addClass('x-hidden hide');
29169         
29170         
29171         
29172         if(Roo.isIE){ // fix IE 1px bogus margin
29173             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29174         }
29175        
29176         
29177         this.frameId = Roo.id();
29178         
29179          
29180         
29181         var iframe = this.owner.wrap.createChild({
29182             tag: 'iframe',
29183             cls: 'form-control', // bootstrap..
29184             id: this.frameId,
29185             name: this.frameId,
29186             frameBorder : 'no',
29187             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
29188         }, this.el
29189         );
29190         
29191         
29192         this.iframe = iframe.dom;
29193
29194         this.assignDocWin();
29195         
29196         this.doc.designMode = 'on';
29197        
29198         this.doc.open();
29199         this.doc.write(this.getDocMarkup());
29200         this.doc.close();
29201
29202         
29203         var task = { // must defer to wait for browser to be ready
29204             run : function(){
29205                 //console.log("run task?" + this.doc.readyState);
29206                 this.assignDocWin();
29207                 if(this.doc.body || this.doc.readyState == 'complete'){
29208                     try {
29209                         this.doc.designMode="on";
29210                         
29211                     } catch (e) {
29212                         return;
29213                     }
29214                     Roo.TaskMgr.stop(task);
29215                     this.initEditor.defer(10, this);
29216                 }
29217             },
29218             interval : 10,
29219             duration: 10000,
29220             scope: this
29221         };
29222         Roo.TaskMgr.start(task);
29223
29224     },
29225
29226     // private
29227     onResize : function(w, h)
29228     {
29229          Roo.log('resize: ' +w + ',' + h );
29230         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29231         if(!this.iframe){
29232             return;
29233         }
29234         if(typeof w == 'number'){
29235             
29236             this.iframe.style.width = w + 'px';
29237         }
29238         if(typeof h == 'number'){
29239             
29240             this.iframe.style.height = h + 'px';
29241             if(this.doc){
29242                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29243             }
29244         }
29245         
29246     },
29247
29248     /**
29249      * Toggles the editor between standard and source edit mode.
29250      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29251      */
29252     toggleSourceEdit : function(sourceEditMode){
29253         
29254         this.sourceEditMode = sourceEditMode === true;
29255         
29256         if(this.sourceEditMode){
29257  
29258             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
29259             
29260         }else{
29261             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29262             //this.iframe.className = '';
29263             this.deferFocus();
29264         }
29265         //this.setSize(this.owner.wrap.getSize());
29266         //this.fireEvent('editmodechange', this, this.sourceEditMode);
29267     },
29268
29269     
29270   
29271
29272     /**
29273      * Protected method that will not generally be called directly. If you need/want
29274      * custom HTML cleanup, this is the method you should override.
29275      * @param {String} html The HTML to be cleaned
29276      * return {String} The cleaned HTML
29277      */
29278     cleanHtml : function(html)
29279     {
29280         html = String(html);
29281         if(html.length > 5){
29282             if(Roo.isSafari){ // strip safari nonsense
29283                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29284             }
29285         }
29286         if(html == '&nbsp;'){
29287             html = '';
29288         }
29289         return html;
29290     },
29291
29292     /**
29293      * HTML Editor -> Textarea
29294      * Protected method that will not generally be called directly. Syncs the contents
29295      * of the editor iframe with the textarea.
29296      */
29297     syncValue : function()
29298     {
29299         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29300         if(this.initialized){
29301             
29302             if (this.undoManager) {
29303                 this.undoManager.addEvent();
29304             }
29305
29306             
29307             var bd = (this.doc.body || this.doc.documentElement);
29308            
29309             
29310             var sel = this.win.getSelection();
29311             
29312             var div = document.createElement('div');
29313             div.innerHTML = bd.innerHTML;
29314             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29315             if (gtx.length > 0) {
29316                 var rm = gtx.item(0).parentNode;
29317                 rm.parentNode.removeChild(rm);
29318             }
29319             
29320            
29321             if (this.enableBlocks) {
29322                 new Roo.htmleditor.FilterBlock({ node : div });
29323             }
29324             
29325             var html = div.innerHTML;
29326             
29327             //?? tidy?
29328             if (this.autoClean) {
29329                 
29330                 new Roo.htmleditor.FilterAttributes({
29331                     node : div,
29332                     attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29333                     attrib_clean : ['href', 'src' ] 
29334                 });
29335                 
29336                 var tidy = new Roo.htmleditor.TidySerializer({
29337                     inner:  true
29338                 });
29339                 html  = tidy.serialize(div);
29340                 
29341             }
29342             
29343             
29344             if(Roo.isSafari){
29345                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29346                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29347                 if(m && m[1]){
29348                     html = '<div style="'+m[0]+'">' + html + '</div>';
29349                 }
29350             }
29351             html = this.cleanHtml(html);
29352             // fix up the special chars.. normaly like back quotes in word...
29353             // however we do not want to do this with chinese..
29354             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29355                 
29356                 var cc = match.charCodeAt();
29357
29358                 // Get the character value, handling surrogate pairs
29359                 if (match.length == 2) {
29360                     // It's a surrogate pair, calculate the Unicode code point
29361                     var high = match.charCodeAt(0) - 0xD800;
29362                     var low  = match.charCodeAt(1) - 0xDC00;
29363                     cc = (high * 0x400) + low + 0x10000;
29364                 }  else if (
29365                     (cc >= 0x4E00 && cc < 0xA000 ) ||
29366                     (cc >= 0x3400 && cc < 0x4E00 ) ||
29367                     (cc >= 0xf900 && cc < 0xfb00 )
29368                 ) {
29369                         return match;
29370                 }  
29371          
29372                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29373                 return "&#" + cc + ";";
29374                 
29375                 
29376             });
29377             
29378             
29379              
29380             if(this.owner.fireEvent('beforesync', this, html) !== false){
29381                 this.el.dom.value = html;
29382                 this.owner.fireEvent('sync', this, html);
29383             }
29384         }
29385     },
29386
29387     /**
29388      * TEXTAREA -> EDITABLE
29389      * Protected method that will not generally be called directly. Pushes the value of the textarea
29390      * into the iframe editor.
29391      */
29392     pushValue : function()
29393     {
29394         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29395         if(this.initialized){
29396             var v = this.el.dom.value.trim();
29397             
29398             
29399             if(this.owner.fireEvent('beforepush', this, v) !== false){
29400                 var d = (this.doc.body || this.doc.documentElement);
29401                 d.innerHTML = v;
29402                  
29403                 this.el.dom.value = d.innerHTML;
29404                 this.owner.fireEvent('push', this, v);
29405             }
29406             if (this.autoClean) {
29407                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29408                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29409             }
29410             if (this.enableBlocks) {
29411                 Roo.htmleditor.Block.initAll(this.doc.body);
29412             }
29413             
29414             this.updateLanguage();
29415             
29416             var lc = this.doc.body.lastChild;
29417             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29418                 // add an extra line at the end.
29419                 this.doc.body.appendChild(this.doc.createElement('br'));
29420             }
29421             
29422             
29423         }
29424     },
29425
29426     // private
29427     deferFocus : function(){
29428         this.focus.defer(10, this);
29429     },
29430
29431     // doc'ed in Field
29432     focus : function(){
29433         if(this.win && !this.sourceEditMode){
29434             this.win.focus();
29435         }else{
29436             this.el.focus();
29437         }
29438     },
29439     
29440     assignDocWin: function()
29441     {
29442         var iframe = this.iframe;
29443         
29444          if(Roo.isIE){
29445             this.doc = iframe.contentWindow.document;
29446             this.win = iframe.contentWindow;
29447         } else {
29448 //            if (!Roo.get(this.frameId)) {
29449 //                return;
29450 //            }
29451 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29452 //            this.win = Roo.get(this.frameId).dom.contentWindow;
29453             
29454             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29455                 return;
29456             }
29457             
29458             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29459             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29460         }
29461     },
29462     
29463     // private
29464     initEditor : function(){
29465         //console.log("INIT EDITOR");
29466         this.assignDocWin();
29467         
29468         
29469         
29470         this.doc.designMode="on";
29471         this.doc.open();
29472         this.doc.write(this.getDocMarkup());
29473         this.doc.close();
29474         
29475         var dbody = (this.doc.body || this.doc.documentElement);
29476         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29477         // this copies styles from the containing element into thsi one..
29478         // not sure why we need all of this..
29479         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29480         
29481         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29482         //ss['background-attachment'] = 'fixed'; // w3c
29483         dbody.bgProperties = 'fixed'; // ie
29484         dbody.setAttribute("translate", "no");
29485         
29486         //Roo.DomHelper.applyStyles(dbody, ss);
29487         Roo.EventManager.on(this.doc, {
29488              
29489             'mouseup': this.onEditorEvent,
29490             'dblclick': this.onEditorEvent,
29491             'click': this.onEditorEvent,
29492             'keyup': this.onEditorEvent,
29493             
29494             buffer:100,
29495             scope: this
29496         });
29497         Roo.EventManager.on(this.doc, {
29498             'paste': this.onPasteEvent,
29499             scope : this
29500         });
29501         if(Roo.isGecko){
29502             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29503         }
29504         //??? needed???
29505         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29506             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29507         }
29508         this.initialized = true;
29509
29510         
29511         // initialize special key events - enter
29512         new Roo.htmleditor.KeyEnter({core : this});
29513         
29514          
29515         
29516         this.owner.fireEvent('initialize', this);
29517         this.pushValue();
29518     },
29519     // this is to prevent a href clicks resulting in a redirect?
29520    
29521     onPasteEvent : function(e,v)
29522     {
29523         // I think we better assume paste is going to be a dirty load of rubish from word..
29524         
29525         // even pasting into a 'email version' of this widget will have to clean up that mess.
29526         var cd = (e.browserEvent.clipboardData || window.clipboardData);
29527         
29528         // check what type of paste - if it's an image, then handle it differently.
29529         if (cd.files && cd.files.length > 0) {
29530             // pasting images?
29531             var urlAPI = (window.createObjectURL && window) || 
29532                 (window.URL && URL.revokeObjectURL && URL) || 
29533                 (window.webkitURL && webkitURL);
29534     
29535             var url = urlAPI.createObjectURL( cd.files[0]);
29536             this.insertAtCursor('<img src=" + url + ">');
29537             return false;
29538         }
29539         if (cd.types.indexOf('text/html') < 0 ) {
29540             return false;
29541         }
29542         var images = [];
29543         var html = cd.getData('text/html'); // clipboard event
29544         if (cd.types.indexOf('text/rtf') > -1) {
29545             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29546             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29547         }
29548         //Roo.log(images);
29549         //Roo.log(imgs);
29550         // fixme..
29551         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29552                        .map(function(g) { return g.toDataURL(); })
29553                        .filter(function(g) { return g != 'about:blank'; });
29554         
29555         //Roo.log(html);
29556         html = this.cleanWordChars(html);
29557         
29558         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29559         
29560         
29561         var sn = this.getParentElement();
29562         // check if d contains a table, and prevent nesting??
29563         //Roo.log(d.getElementsByTagName('table'));
29564         //Roo.log(sn);
29565         //Roo.log(sn.closest('table'));
29566         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29567             e.preventDefault();
29568             this.insertAtCursor("You can not nest tables");
29569             //Roo.log("prevent?"); // fixme - 
29570             return false;
29571         }
29572         
29573         
29574         
29575         if (images.length > 0) {
29576             // replace all v:imagedata - with img.
29577             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
29578             Roo.each(ar, function(node) {
29579                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
29580                 node.parentNode.removeChild(node);
29581             });
29582             
29583             
29584             Roo.each(d.getElementsByTagName('img'), function(img, i) {
29585                 img.setAttribute('src', images[i]);
29586             });
29587         }
29588         if (this.autoClean) {
29589             new Roo.htmleditor.FilterWord({ node : d });
29590             
29591             new Roo.htmleditor.FilterStyleToTag({ node : d });
29592             new Roo.htmleditor.FilterAttributes({
29593                 node : d,
29594                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29595                 attrib_clean : ['href', 'src' ] 
29596             });
29597             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29598             // should be fonts..
29599             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
29600             new Roo.htmleditor.FilterParagraph({ node : d });
29601             new Roo.htmleditor.FilterSpan({ node : d });
29602             new Roo.htmleditor.FilterLongBr({ node : d });
29603             new Roo.htmleditor.FilterComment({ node : d });
29604             
29605             
29606         }
29607         if (this.enableBlocks) {
29608                 
29609             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29610                 if (img.closest('figure')) { // assume!! that it's aready
29611                     return;
29612                 }
29613                 var fig  = new Roo.htmleditor.BlockFigure({
29614                     image_src  : img.src
29615                 });
29616                 fig.updateElement(img); // replace it..
29617                 
29618             });
29619         }
29620         
29621         
29622         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
29623         if (this.enableBlocks) {
29624             Roo.htmleditor.Block.initAll(this.doc.body);
29625         }
29626          
29627         
29628         e.preventDefault();
29629         return false;
29630         // default behaveiour should be our local cleanup paste? (optional?)
29631         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29632         //this.owner.fireEvent('paste', e, v);
29633     },
29634     // private
29635     onDestroy : function(){
29636         
29637         
29638         
29639         if(this.rendered){
29640             
29641             //for (var i =0; i < this.toolbars.length;i++) {
29642             //    // fixme - ask toolbars for heights?
29643             //    this.toolbars[i].onDestroy();
29644            // }
29645             
29646             //this.wrap.dom.innerHTML = '';
29647             //this.wrap.remove();
29648         }
29649     },
29650
29651     // private
29652     onFirstFocus : function(){
29653         
29654         this.assignDocWin();
29655         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29656         
29657         this.activated = true;
29658          
29659     
29660         if(Roo.isGecko){ // prevent silly gecko errors
29661             this.win.focus();
29662             var s = this.win.getSelection();
29663             if(!s.focusNode || s.focusNode.nodeType != 3){
29664                 var r = s.getRangeAt(0);
29665                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29666                 r.collapse(true);
29667                 this.deferFocus();
29668             }
29669             try{
29670                 this.execCmd('useCSS', true);
29671                 this.execCmd('styleWithCSS', false);
29672             }catch(e){}
29673         }
29674         this.owner.fireEvent('activate', this);
29675     },
29676
29677     // private
29678     adjustFont: function(btn){
29679         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29680         //if(Roo.isSafari){ // safari
29681         //    adjust *= 2;
29682        // }
29683         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29684         if(Roo.isSafari){ // safari
29685             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29686             v =  (v < 10) ? 10 : v;
29687             v =  (v > 48) ? 48 : v;
29688             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29689             
29690         }
29691         
29692         
29693         v = Math.max(1, v+adjust);
29694         
29695         this.execCmd('FontSize', v  );
29696     },
29697
29698     onEditorEvent : function(e)
29699     {
29700          
29701         
29702         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29703             return; // we do not handle this.. (undo manager does..)
29704         }
29705         // in theory this detects if the last element is not a br, then we try and do that.
29706         // its so clicking in space at bottom triggers adding a br and moving the cursor.
29707         if (e &&
29708             e.target.nodeName == 'BODY' &&
29709             e.type == "mouseup" &&
29710             this.doc.body.lastChild
29711            ) {
29712             var lc = this.doc.body.lastChild;
29713             // gtx-trans is google translate plugin adding crap.
29714             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29715                 lc = lc.previousSibling;
29716             }
29717             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29718             // if last element is <BR> - then dont do anything.
29719             
29720                 var ns = this.doc.createElement('br');
29721                 this.doc.body.appendChild(ns);
29722                 range = this.doc.createRange();
29723                 range.setStartAfter(ns);
29724                 range.collapse(true);
29725                 var sel = this.win.getSelection();
29726                 sel.removeAllRanges();
29727                 sel.addRange(range);
29728             }
29729         }
29730         
29731         
29732         
29733         this.fireEditorEvent(e);
29734       //  this.updateToolbar();
29735         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29736     },
29737     
29738     fireEditorEvent: function(e)
29739     {
29740         this.owner.fireEvent('editorevent', this, e);
29741     },
29742
29743     insertTag : function(tg)
29744     {
29745         // could be a bit smarter... -> wrap the current selected tRoo..
29746         if (tg.toLowerCase() == 'span' ||
29747             tg.toLowerCase() == 'code' ||
29748             tg.toLowerCase() == 'sup' ||
29749             tg.toLowerCase() == 'sub' 
29750             ) {
29751             
29752             range = this.createRange(this.getSelection());
29753             var wrappingNode = this.doc.createElement(tg.toLowerCase());
29754             wrappingNode.appendChild(range.extractContents());
29755             range.insertNode(wrappingNode);
29756
29757             return;
29758             
29759             
29760             
29761         }
29762         this.execCmd("formatblock",   tg);
29763         this.undoManager.addEvent(); 
29764     },
29765     
29766     insertText : function(txt)
29767     {
29768         
29769         
29770         var range = this.createRange();
29771         range.deleteContents();
29772                //alert(Sender.getAttribute('label'));
29773                
29774         range.insertNode(this.doc.createTextNode(txt));
29775         this.undoManager.addEvent();
29776     } ,
29777     
29778      
29779
29780     /**
29781      * Executes a Midas editor command on the editor document and performs necessary focus and
29782      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29783      * @param {String} cmd The Midas command
29784      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29785      */
29786     relayCmd : function(cmd, value)
29787     {
29788         
29789         switch (cmd) {
29790             case 'justifyleft':
29791             case 'justifyright':
29792             case 'justifycenter':
29793                 // if we are in a cell, then we will adjust the
29794                 var n = this.getParentElement();
29795                 var td = n.closest('td');
29796                 if (td) {
29797                     var bl = Roo.htmleditor.Block.factory(td);
29798                     bl.textAlign = cmd.replace('justify','');
29799                     bl.updateElement();
29800                     this.owner.fireEvent('editorevent', this);
29801                     return;
29802                 }
29803                 this.execCmd('styleWithCSS', true); // 
29804                 break;
29805             case 'bold':
29806             case 'italic':
29807                 // if there is no selection, then we insert, and set the curson inside it..
29808                 this.execCmd('styleWithCSS', false); 
29809                 break;
29810                 
29811         
29812             default:
29813                 break;
29814         }
29815         
29816         
29817         this.win.focus();
29818         this.execCmd(cmd, value);
29819         this.owner.fireEvent('editorevent', this);
29820         //this.updateToolbar();
29821         this.owner.deferFocus();
29822     },
29823
29824     /**
29825      * Executes a Midas editor command directly on the editor document.
29826      * For visual commands, you should use {@link #relayCmd} instead.
29827      * <b>This should only be called after the editor is initialized.</b>
29828      * @param {String} cmd The Midas command
29829      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29830      */
29831     execCmd : function(cmd, value){
29832         this.doc.execCommand(cmd, false, value === undefined ? null : value);
29833         this.syncValue();
29834     },
29835  
29836  
29837    
29838     /**
29839      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29840      * to insert tRoo.
29841      * @param {String} text | dom node.. 
29842      */
29843     insertAtCursor : function(text)
29844     {
29845         
29846         if(!this.activated){
29847             return;
29848         }
29849          
29850         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29851             this.win.focus();
29852             
29853             
29854             // from jquery ui (MIT licenced)
29855             var range, node;
29856             var win = this.win;
29857             
29858             if (win.getSelection && win.getSelection().getRangeAt) {
29859                 
29860                 // delete the existing?
29861                 
29862                 this.createRange(this.getSelection()).deleteContents();
29863                 range = win.getSelection().getRangeAt(0);
29864                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29865                 range.insertNode(node);
29866                 range = range.cloneRange();
29867                 range.collapse(false);
29868                  
29869                 win.getSelection().removeAllRanges();
29870                 win.getSelection().addRange(range);
29871                 
29872                 
29873                 
29874             } else if (win.document.selection && win.document.selection.createRange) {
29875                 // no firefox support
29876                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29877                 win.document.selection.createRange().pasteHTML(txt);
29878             
29879             } else {
29880                 // no firefox support
29881                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29882                 this.execCmd('InsertHTML', txt);
29883             } 
29884             this.syncValue();
29885             
29886             this.deferFocus();
29887         }
29888     },
29889  // private
29890     mozKeyPress : function(e){
29891         if(e.ctrlKey){
29892             var c = e.getCharCode(), cmd;
29893           
29894             if(c > 0){
29895                 c = String.fromCharCode(c).toLowerCase();
29896                 switch(c){
29897                     case 'b':
29898                         cmd = 'bold';
29899                         break;
29900                     case 'i':
29901                         cmd = 'italic';
29902                         break;
29903                     
29904                     case 'u':
29905                         cmd = 'underline';
29906                         break;
29907                     
29908                     //case 'v':
29909                       //  this.cleanUpPaste.defer(100, this);
29910                       //  return;
29911                         
29912                 }
29913                 if(cmd){
29914                     
29915                     this.relayCmd(cmd);
29916                     //this.win.focus();
29917                     //this.execCmd(cmd);
29918                     //this.deferFocus();
29919                     e.preventDefault();
29920                 }
29921                 
29922             }
29923         }
29924     },
29925
29926     // private
29927     fixKeys : function(){ // load time branching for fastest keydown performance
29928         
29929         
29930         if(Roo.isIE){
29931             return function(e){
29932                 var k = e.getKey(), r;
29933                 if(k == e.TAB){
29934                     e.stopEvent();
29935                     r = this.doc.selection.createRange();
29936                     if(r){
29937                         r.collapse(true);
29938                         r.pasteHTML('&#160;&#160;&#160;&#160;');
29939                         this.deferFocus();
29940                     }
29941                     return;
29942                 }
29943                 /// this is handled by Roo.htmleditor.KeyEnter
29944                  /*
29945                 if(k == e.ENTER){
29946                     r = this.doc.selection.createRange();
29947                     if(r){
29948                         var target = r.parentElement();
29949                         if(!target || target.tagName.toLowerCase() != 'li'){
29950                             e.stopEvent();
29951                             r.pasteHTML('<br/>');
29952                             r.collapse(false);
29953                             r.select();
29954                         }
29955                     }
29956                 }
29957                 */
29958                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29959                 //    this.cleanUpPaste.defer(100, this);
29960                 //    return;
29961                 //}
29962                 
29963                 
29964             };
29965         }else if(Roo.isOpera){
29966             return function(e){
29967                 var k = e.getKey();
29968                 if(k == e.TAB){
29969                     e.stopEvent();
29970                     this.win.focus();
29971                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
29972                     this.deferFocus();
29973                 }
29974                
29975                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29976                 //    this.cleanUpPaste.defer(100, this);
29977                  //   return;
29978                 //}
29979                 
29980             };
29981         }else if(Roo.isSafari){
29982             return function(e){
29983                 var k = e.getKey();
29984                 
29985                 if(k == e.TAB){
29986                     e.stopEvent();
29987                     this.execCmd('InsertText','\t');
29988                     this.deferFocus();
29989                     return;
29990                 }
29991                  this.mozKeyPress(e);
29992                 
29993                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29994                  //   this.cleanUpPaste.defer(100, this);
29995                  //   return;
29996                // }
29997                 
29998              };
29999         }
30000     }(),
30001     
30002     getAllAncestors: function()
30003     {
30004         var p = this.getSelectedNode();
30005         var a = [];
30006         if (!p) {
30007             a.push(p); // push blank onto stack..
30008             p = this.getParentElement();
30009         }
30010         
30011         
30012         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
30013             a.push(p);
30014             p = p.parentNode;
30015         }
30016         a.push(this.doc.body);
30017         return a;
30018     },
30019     lastSel : false,
30020     lastSelNode : false,
30021     
30022     
30023     getSelection : function() 
30024     {
30025         this.assignDocWin();
30026         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
30027     },
30028     /**
30029      * Select a dom node
30030      * @param {DomElement} node the node to select
30031      */
30032     selectNode : function(node, collapse)
30033     {
30034         var nodeRange = node.ownerDocument.createRange();
30035         try {
30036             nodeRange.selectNode(node);
30037         } catch (e) {
30038             nodeRange.selectNodeContents(node);
30039         }
30040         if (collapse === true) {
30041             nodeRange.collapse(true);
30042         }
30043         //
30044         var s = this.win.getSelection();
30045         s.removeAllRanges();
30046         s.addRange(nodeRange);
30047     },
30048     
30049     getSelectedNode: function() 
30050     {
30051         // this may only work on Gecko!!!
30052         
30053         // should we cache this!!!!
30054         
30055          
30056          
30057         var range = this.createRange(this.getSelection()).cloneRange();
30058         
30059         if (Roo.isIE) {
30060             var parent = range.parentElement();
30061             while (true) {
30062                 var testRange = range.duplicate();
30063                 testRange.moveToElementText(parent);
30064                 if (testRange.inRange(range)) {
30065                     break;
30066                 }
30067                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
30068                     break;
30069                 }
30070                 parent = parent.parentElement;
30071             }
30072             return parent;
30073         }
30074         
30075         // is ancestor a text element.
30076         var ac =  range.commonAncestorContainer;
30077         if (ac.nodeType == 3) {
30078             ac = ac.parentNode;
30079         }
30080         
30081         var ar = ac.childNodes;
30082          
30083         var nodes = [];
30084         var other_nodes = [];
30085         var has_other_nodes = false;
30086         for (var i=0;i<ar.length;i++) {
30087             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
30088                 continue;
30089             }
30090             // fullly contained node.
30091             
30092             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
30093                 nodes.push(ar[i]);
30094                 continue;
30095             }
30096             
30097             // probably selected..
30098             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
30099                 other_nodes.push(ar[i]);
30100                 continue;
30101             }
30102             // outer..
30103             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
30104                 continue;
30105             }
30106             
30107             
30108             has_other_nodes = true;
30109         }
30110         if (!nodes.length && other_nodes.length) {
30111             nodes= other_nodes;
30112         }
30113         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
30114             return false;
30115         }
30116         
30117         return nodes[0];
30118     },
30119     
30120     
30121     createRange: function(sel)
30122     {
30123         // this has strange effects when using with 
30124         // top toolbar - not sure if it's a great idea.
30125         //this.editor.contentWindow.focus();
30126         if (typeof sel != "undefined") {
30127             try {
30128                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
30129             } catch(e) {
30130                 return this.doc.createRange();
30131             }
30132         } else {
30133             return this.doc.createRange();
30134         }
30135     },
30136     getParentElement: function()
30137     {
30138         
30139         this.assignDocWin();
30140         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
30141         
30142         var range = this.createRange(sel);
30143          
30144         try {
30145             var p = range.commonAncestorContainer;
30146             while (p.nodeType == 3) { // text node
30147                 p = p.parentNode;
30148             }
30149             return p;
30150         } catch (e) {
30151             return null;
30152         }
30153     
30154     },
30155     /***
30156      *
30157      * Range intersection.. the hard stuff...
30158      *  '-1' = before
30159      *  '0' = hits..
30160      *  '1' = after.
30161      *         [ -- selected range --- ]
30162      *   [fail]                        [fail]
30163      *
30164      *    basically..
30165      *      if end is before start or  hits it. fail.
30166      *      if start is after end or hits it fail.
30167      *
30168      *   if either hits (but other is outside. - then it's not 
30169      *   
30170      *    
30171      **/
30172     
30173     
30174     // @see http://www.thismuchiknow.co.uk/?p=64.
30175     rangeIntersectsNode : function(range, node)
30176     {
30177         var nodeRange = node.ownerDocument.createRange();
30178         try {
30179             nodeRange.selectNode(node);
30180         } catch (e) {
30181             nodeRange.selectNodeContents(node);
30182         }
30183     
30184         var rangeStartRange = range.cloneRange();
30185         rangeStartRange.collapse(true);
30186     
30187         var rangeEndRange = range.cloneRange();
30188         rangeEndRange.collapse(false);
30189     
30190         var nodeStartRange = nodeRange.cloneRange();
30191         nodeStartRange.collapse(true);
30192     
30193         var nodeEndRange = nodeRange.cloneRange();
30194         nodeEndRange.collapse(false);
30195     
30196         return rangeStartRange.compareBoundaryPoints(
30197                  Range.START_TO_START, nodeEndRange) == -1 &&
30198                rangeEndRange.compareBoundaryPoints(
30199                  Range.START_TO_START, nodeStartRange) == 1;
30200         
30201          
30202     },
30203     rangeCompareNode : function(range, node)
30204     {
30205         var nodeRange = node.ownerDocument.createRange();
30206         try {
30207             nodeRange.selectNode(node);
30208         } catch (e) {
30209             nodeRange.selectNodeContents(node);
30210         }
30211         
30212         
30213         range.collapse(true);
30214     
30215         nodeRange.collapse(true);
30216      
30217         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30218         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
30219          
30220         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30221         
30222         var nodeIsBefore   =  ss == 1;
30223         var nodeIsAfter    = ee == -1;
30224         
30225         if (nodeIsBefore && nodeIsAfter) {
30226             return 0; // outer
30227         }
30228         if (!nodeIsBefore && nodeIsAfter) {
30229             return 1; //right trailed.
30230         }
30231         
30232         if (nodeIsBefore && !nodeIsAfter) {
30233             return 2;  // left trailed.
30234         }
30235         // fully contined.
30236         return 3;
30237     },
30238  
30239     cleanWordChars : function(input) {// change the chars to hex code
30240         
30241        var swapCodes  = [ 
30242             [    8211, "&#8211;" ], 
30243             [    8212, "&#8212;" ], 
30244             [    8216,  "'" ],  
30245             [    8217, "'" ],  
30246             [    8220, '"' ],  
30247             [    8221, '"' ],  
30248             [    8226, "*" ],  
30249             [    8230, "..." ]
30250         ]; 
30251         var output = input;
30252         Roo.each(swapCodes, function(sw) { 
30253             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30254             
30255             output = output.replace(swapper, sw[1]);
30256         });
30257         
30258         return output;
30259     },
30260     
30261      
30262     
30263         
30264     
30265     cleanUpChild : function (node)
30266     {
30267         
30268         new Roo.htmleditor.FilterComment({node : node});
30269         new Roo.htmleditor.FilterAttributes({
30270                 node : node,
30271                 attrib_black : this.ablack,
30272                 attrib_clean : this.aclean,
30273                 style_white : this.cwhite,
30274                 style_black : this.cblack
30275         });
30276         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30277         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30278          
30279         
30280     },
30281     
30282     /**
30283      * Clean up MS wordisms...
30284      * @deprecated - use filter directly
30285      */
30286     cleanWord : function(node)
30287     {
30288         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30289         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
30290         
30291     },
30292    
30293     
30294     /**
30295
30296      * @deprecated - use filters
30297      */
30298     cleanTableWidths : function(node)
30299     {
30300         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30301         
30302  
30303     },
30304     
30305      
30306         
30307     applyBlacklists : function()
30308     {
30309         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
30310         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
30311         
30312         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
30313         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
30314         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
30315         
30316         this.white = [];
30317         this.black = [];
30318         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30319             if (b.indexOf(tag) > -1) {
30320                 return;
30321             }
30322             this.white.push(tag);
30323             
30324         }, this);
30325         
30326         Roo.each(w, function(tag) {
30327             if (b.indexOf(tag) > -1) {
30328                 return;
30329             }
30330             if (this.white.indexOf(tag) > -1) {
30331                 return;
30332             }
30333             this.white.push(tag);
30334             
30335         }, this);
30336         
30337         
30338         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30339             if (w.indexOf(tag) > -1) {
30340                 return;
30341             }
30342             this.black.push(tag);
30343             
30344         }, this);
30345         
30346         Roo.each(b, function(tag) {
30347             if (w.indexOf(tag) > -1) {
30348                 return;
30349             }
30350             if (this.black.indexOf(tag) > -1) {
30351                 return;
30352             }
30353             this.black.push(tag);
30354             
30355         }, this);
30356         
30357         
30358         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
30359         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
30360         
30361         this.cwhite = [];
30362         this.cblack = [];
30363         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30364             if (b.indexOf(tag) > -1) {
30365                 return;
30366             }
30367             this.cwhite.push(tag);
30368             
30369         }, this);
30370         
30371         Roo.each(w, function(tag) {
30372             if (b.indexOf(tag) > -1) {
30373                 return;
30374             }
30375             if (this.cwhite.indexOf(tag) > -1) {
30376                 return;
30377             }
30378             this.cwhite.push(tag);
30379             
30380         }, this);
30381         
30382         
30383         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30384             if (w.indexOf(tag) > -1) {
30385                 return;
30386             }
30387             this.cblack.push(tag);
30388             
30389         }, this);
30390         
30391         Roo.each(b, function(tag) {
30392             if (w.indexOf(tag) > -1) {
30393                 return;
30394             }
30395             if (this.cblack.indexOf(tag) > -1) {
30396                 return;
30397             }
30398             this.cblack.push(tag);
30399             
30400         }, this);
30401     },
30402     
30403     setStylesheets : function(stylesheets)
30404     {
30405         if(typeof(stylesheets) == 'string'){
30406             Roo.get(this.iframe.contentDocument.head).createChild({
30407                 tag : 'link',
30408                 rel : 'stylesheet',
30409                 type : 'text/css',
30410                 href : stylesheets
30411             });
30412             
30413             return;
30414         }
30415         var _this = this;
30416      
30417         Roo.each(stylesheets, function(s) {
30418             if(!s.length){
30419                 return;
30420             }
30421             
30422             Roo.get(_this.iframe.contentDocument.head).createChild({
30423                 tag : 'link',
30424                 rel : 'stylesheet',
30425                 type : 'text/css',
30426                 href : s
30427             });
30428         });
30429
30430         
30431     },
30432     
30433     
30434     updateLanguage : function()
30435     {
30436         if (!this.iframe || !this.iframe.contentDocument) {
30437             return;
30438         }
30439         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30440     },
30441     
30442     
30443     removeStylesheets : function()
30444     {
30445         var _this = this;
30446         
30447         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30448             s.remove();
30449         });
30450     },
30451     
30452     setStyle : function(style)
30453     {
30454         Roo.get(this.iframe.contentDocument.head).createChild({
30455             tag : 'style',
30456             type : 'text/css',
30457             html : style
30458         });
30459
30460         return;
30461     }
30462     
30463     // hide stuff that is not compatible
30464     /**
30465      * @event blur
30466      * @hide
30467      */
30468     /**
30469      * @event change
30470      * @hide
30471      */
30472     /**
30473      * @event focus
30474      * @hide
30475      */
30476     /**
30477      * @event specialkey
30478      * @hide
30479      */
30480     /**
30481      * @cfg {String} fieldClass @hide
30482      */
30483     /**
30484      * @cfg {String} focusClass @hide
30485      */
30486     /**
30487      * @cfg {String} autoCreate @hide
30488      */
30489     /**
30490      * @cfg {String} inputType @hide
30491      */
30492     /**
30493      * @cfg {String} invalidClass @hide
30494      */
30495     /**
30496      * @cfg {String} invalidText @hide
30497      */
30498     /**
30499      * @cfg {String} msgFx @hide
30500      */
30501     /**
30502      * @cfg {String} validateOnBlur @hide
30503      */
30504 });
30505
30506 Roo.HtmlEditorCore.white = [
30507         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30508         
30509        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
30510        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
30511        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
30512        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
30513        'TABLE',   'UL',         'XMP', 
30514        
30515        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
30516       'THEAD',   'TR', 
30517      
30518       'DIR', 'MENU', 'OL', 'UL', 'DL',
30519        
30520       'EMBED',  'OBJECT'
30521 ];
30522
30523
30524 Roo.HtmlEditorCore.black = [
30525     //    'embed',  'object', // enable - backend responsiblity to clean thiese
30526         'APPLET', // 
30527         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
30528         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
30529         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
30530         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
30531         //'FONT' // CLEAN LATER..
30532         'COLGROUP', 'COL'   // messy tables.
30533         
30534         
30535 ];
30536 Roo.HtmlEditorCore.clean = [ // ?? needed???
30537      'SCRIPT', 'STYLE', 'TITLE', 'XML'
30538 ];
30539 Roo.HtmlEditorCore.tag_remove = [
30540     'FONT', 'TBODY'  
30541 ];
30542 // attributes..
30543
30544 Roo.HtmlEditorCore.ablack = [
30545     'on'
30546 ];
30547     
30548 Roo.HtmlEditorCore.aclean = [ 
30549     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
30550 ];
30551
30552 // protocols..
30553 Roo.HtmlEditorCore.pwhite= [
30554         'http',  'https',  'mailto'
30555 ];
30556
30557 // white listed style attributes.
30558 Roo.HtmlEditorCore.cwhite= [
30559       //  'text-align', /// default is to allow most things..
30560       
30561          
30562 //        'font-size'//??
30563 ];
30564
30565 // black listed style attributes.
30566 Roo.HtmlEditorCore.cblack= [
30567       //  'font-size' -- this can be set by the project 
30568 ];
30569
30570
30571
30572
30573     /*
30574  * - LGPL
30575  *
30576  * HtmlEditor
30577  * 
30578  */
30579
30580 /**
30581  * @class Roo.bootstrap.form.HtmlEditor
30582  * @extends Roo.bootstrap.form.TextArea
30583  * Bootstrap HtmlEditor class
30584
30585  * @constructor
30586  * Create a new HtmlEditor
30587  * @param {Object} config The config object
30588  */
30589
30590 Roo.bootstrap.form.HtmlEditor = function(config){
30591     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30592     if (!this.toolbars) {
30593         this.toolbars = [];
30594     }
30595     
30596     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30597     this.addEvents({
30598             /**
30599              * @event initialize
30600              * Fires when the editor is fully initialized (including the iframe)
30601              * @param {HtmlEditor} this
30602              */
30603             initialize: true,
30604             /**
30605              * @event activate
30606              * Fires when the editor is first receives the focus. Any insertion must wait
30607              * until after this event.
30608              * @param {HtmlEditor} this
30609              */
30610             activate: true,
30611              /**
30612              * @event beforesync
30613              * Fires before the textarea is updated with content from the editor iframe. Return false
30614              * to cancel the sync.
30615              * @param {HtmlEditor} this
30616              * @param {String} html
30617              */
30618             beforesync: true,
30619              /**
30620              * @event beforepush
30621              * Fires before the iframe editor is updated with content from the textarea. Return false
30622              * to cancel the push.
30623              * @param {HtmlEditor} this
30624              * @param {String} html
30625              */
30626             beforepush: true,
30627              /**
30628              * @event sync
30629              * Fires when the textarea is updated with content from the editor iframe.
30630              * @param {HtmlEditor} this
30631              * @param {String} html
30632              */
30633             sync: true,
30634              /**
30635              * @event push
30636              * Fires when the iframe editor is updated with content from the textarea.
30637              * @param {HtmlEditor} this
30638              * @param {String} html
30639              */
30640             push: true,
30641              /**
30642              * @event editmodechange
30643              * Fires when the editor switches edit modes
30644              * @param {HtmlEditor} this
30645              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30646              */
30647             editmodechange: true,
30648             /**
30649              * @event editorevent
30650              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30651              * @param {HtmlEditor} this
30652              */
30653             editorevent: true,
30654             /**
30655              * @event firstfocus
30656              * Fires when on first focus - needed by toolbars..
30657              * @param {HtmlEditor} this
30658              */
30659             firstfocus: true,
30660             /**
30661              * @event autosave
30662              * Auto save the htmlEditor value as a file into Events
30663              * @param {HtmlEditor} this
30664              */
30665             autosave: true,
30666             /**
30667              * @event savedpreview
30668              * preview the saved version of htmlEditor
30669              * @param {HtmlEditor} this
30670              */
30671             savedpreview: true
30672         });
30673 };
30674
30675
30676 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
30677     
30678     
30679       /**
30680      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30681      */
30682     toolbars : false,
30683     
30684      /**
30685     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30686     */
30687     btns : [],
30688    
30689      /**
30690      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30691      *                        Roo.resizable.
30692      */
30693     resizable : false,
30694      /**
30695      * @cfg {Number} height (in pixels)
30696      */   
30697     height: 300,
30698    /**
30699      * @cfg {Number} width (in pixels)
30700      */   
30701     width: false,
30702     
30703     /**
30704      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30705      * 
30706      */
30707     stylesheets: false,
30708     
30709     // id of frame..
30710     frameId: false,
30711     
30712     // private properties
30713     validationEvent : false,
30714     deferHeight: true,
30715     initialized : false,
30716     activated : false,
30717     
30718     onFocus : Roo.emptyFn,
30719     iframePad:3,
30720     hideMode:'offsets',
30721     
30722     tbContainer : false,
30723     
30724     bodyCls : '',
30725     
30726     toolbarContainer :function() {
30727         return this.wrap.select('.x-html-editor-tb',true).first();
30728     },
30729
30730     /**
30731      * Protected method that will not generally be called directly. It
30732      * is called when the editor creates its toolbar. Override this method if you need to
30733      * add custom toolbar buttons.
30734      * @param {HtmlEditor} editor
30735      */
30736     createToolbar : function(){
30737         Roo.log('renewing');
30738         Roo.log("create toolbars");
30739         
30740         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30741         this.toolbars[0].render(this.toolbarContainer());
30742         
30743         return;
30744         
30745 //        if (!editor.toolbars || !editor.toolbars.length) {
30746 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30747 //        }
30748 //        
30749 //        for (var i =0 ; i < editor.toolbars.length;i++) {
30750 //            editor.toolbars[i] = Roo.factory(
30751 //                    typeof(editor.toolbars[i]) == 'string' ?
30752 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
30753 //                Roo.bootstrap.form.HtmlEditor);
30754 //            editor.toolbars[i].init(editor);
30755 //        }
30756     },
30757
30758      
30759     // private
30760     onRender : function(ct, position)
30761     {
30762        // Roo.log("Call onRender: " + this.xtype);
30763         var _t = this;
30764         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30765       
30766         this.wrap = this.inputEl().wrap({
30767             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30768         });
30769         
30770         this.editorcore.onRender(ct, position);
30771          
30772         if (this.resizable) {
30773             this.resizeEl = new Roo.Resizable(this.wrap, {
30774                 pinned : true,
30775                 wrap: true,
30776                 dynamic : true,
30777                 minHeight : this.height,
30778                 height: this.height,
30779                 handles : this.resizable,
30780                 width: this.width,
30781                 listeners : {
30782                     resize : function(r, w, h) {
30783                         _t.onResize(w,h); // -something
30784                     }
30785                 }
30786             });
30787             
30788         }
30789         this.createToolbar(this);
30790        
30791         
30792         if(!this.width && this.resizable){
30793             this.setSize(this.wrap.getSize());
30794         }
30795         if (this.resizeEl) {
30796             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30797             // should trigger onReize..
30798         }
30799         
30800     },
30801
30802     // private
30803     onResize : function(w, h)
30804     {
30805         Roo.log('resize: ' +w + ',' + h );
30806         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30807         var ew = false;
30808         var eh = false;
30809         
30810         if(this.inputEl() ){
30811             if(typeof w == 'number'){
30812                 var aw = w - this.wrap.getFrameWidth('lr');
30813                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30814                 ew = aw;
30815             }
30816             if(typeof h == 'number'){
30817                  var tbh = -11;  // fixme it needs to tool bar size!
30818                 for (var i =0; i < this.toolbars.length;i++) {
30819                     // fixme - ask toolbars for heights?
30820                     tbh += this.toolbars[i].el.getHeight();
30821                     //if (this.toolbars[i].footer) {
30822                     //    tbh += this.toolbars[i].footer.el.getHeight();
30823                     //}
30824                 }
30825               
30826                 
30827                 
30828                 
30829                 
30830                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30831                 ah -= 5; // knock a few pixes off for look..
30832                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30833                 var eh = ah;
30834             }
30835         }
30836         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30837         this.editorcore.onResize(ew,eh);
30838         
30839     },
30840
30841     /**
30842      * Toggles the editor between standard and source edit mode.
30843      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30844      */
30845     toggleSourceEdit : function(sourceEditMode)
30846     {
30847         this.editorcore.toggleSourceEdit(sourceEditMode);
30848         
30849         if(this.editorcore.sourceEditMode){
30850             Roo.log('editor - showing textarea');
30851             
30852 //            Roo.log('in');
30853 //            Roo.log(this.syncValue());
30854             this.syncValue();
30855             this.inputEl().removeClass(['hide', 'x-hidden']);
30856             this.inputEl().dom.removeAttribute('tabIndex');
30857             this.inputEl().focus();
30858         }else{
30859             Roo.log('editor - hiding textarea');
30860 //            Roo.log('out')
30861 //            Roo.log(this.pushValue()); 
30862             this.pushValue();
30863             
30864             this.inputEl().addClass(['hide', 'x-hidden']);
30865             this.inputEl().dom.setAttribute('tabIndex', -1);
30866             //this.deferFocus();
30867         }
30868          
30869         if(this.resizable){
30870             this.setSize(this.wrap.getSize());
30871         }
30872         
30873         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30874     },
30875  
30876     // private (for BoxComponent)
30877     adjustSize : Roo.BoxComponent.prototype.adjustSize,
30878
30879     // private (for BoxComponent)
30880     getResizeEl : function(){
30881         return this.wrap;
30882     },
30883
30884     // private (for BoxComponent)
30885     getPositionEl : function(){
30886         return this.wrap;
30887     },
30888
30889     // private
30890     initEvents : function(){
30891         this.originalValue = this.getValue();
30892     },
30893
30894 //    /**
30895 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30896 //     * @method
30897 //     */
30898 //    markInvalid : Roo.emptyFn,
30899 //    /**
30900 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30901 //     * @method
30902 //     */
30903 //    clearInvalid : Roo.emptyFn,
30904
30905     setValue : function(v){
30906         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30907         this.editorcore.pushValue();
30908     },
30909
30910      
30911     // private
30912     deferFocus : function(){
30913         this.focus.defer(10, this);
30914     },
30915
30916     // doc'ed in Field
30917     focus : function(){
30918         this.editorcore.focus();
30919         
30920     },
30921       
30922
30923     // private
30924     onDestroy : function(){
30925         
30926         
30927         
30928         if(this.rendered){
30929             
30930             for (var i =0; i < this.toolbars.length;i++) {
30931                 // fixme - ask toolbars for heights?
30932                 this.toolbars[i].onDestroy();
30933             }
30934             
30935             this.wrap.dom.innerHTML = '';
30936             this.wrap.remove();
30937         }
30938     },
30939
30940     // private
30941     onFirstFocus : function(){
30942         //Roo.log("onFirstFocus");
30943         this.editorcore.onFirstFocus();
30944          for (var i =0; i < this.toolbars.length;i++) {
30945             this.toolbars[i].onFirstFocus();
30946         }
30947         
30948     },
30949     
30950     // private
30951     syncValue : function()
30952     {   
30953         this.editorcore.syncValue();
30954     },
30955     
30956     pushValue : function()
30957     {   
30958         this.editorcore.pushValue();
30959     }
30960      
30961     
30962     // hide stuff that is not compatible
30963     /**
30964      * @event blur
30965      * @hide
30966      */
30967     /**
30968      * @event change
30969      * @hide
30970      */
30971     /**
30972      * @event focus
30973      * @hide
30974      */
30975     /**
30976      * @event specialkey
30977      * @hide
30978      */
30979     /**
30980      * @cfg {String} fieldClass @hide
30981      */
30982     /**
30983      * @cfg {String} focusClass @hide
30984      */
30985     /**
30986      * @cfg {String} autoCreate @hide
30987      */
30988     /**
30989      * @cfg {String} inputType @hide
30990      */
30991      
30992     /**
30993      * @cfg {String} invalidText @hide
30994      */
30995     /**
30996      * @cfg {String} msgFx @hide
30997      */
30998     /**
30999      * @cfg {String} validateOnBlur @hide
31000      */
31001 });
31002  
31003     
31004    
31005    
31006    
31007       
31008 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
31009 /**
31010  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
31011  * @parent Roo.bootstrap.form.HtmlEditor
31012  * @extends Roo.bootstrap.nav.Simplebar
31013  * Basic Toolbar
31014  * 
31015  * @example
31016  * Usage:
31017  *
31018  new Roo.bootstrap.form.HtmlEditor({
31019     ....
31020     toolbars : [
31021         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
31022             disable : { fonts: 1 , format: 1, ..., ... , ...],
31023             btns : [ .... ]
31024         })
31025     }
31026      
31027  * 
31028  * @cfg {Object} disable List of elements to disable..
31029  * @cfg {Array} btns List of additional buttons.
31030  * 
31031  * 
31032  * NEEDS Extra CSS? 
31033  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
31034  */
31035  
31036 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
31037 {
31038     
31039     Roo.apply(this, config);
31040     
31041     // default disabled, based on 'good practice'..
31042     this.disable = this.disable || {};
31043     Roo.applyIf(this.disable, {
31044         fontSize : true,
31045         colors : true,
31046         specialElements : true
31047     });
31048     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
31049     
31050     this.editor = config.editor;
31051     this.editorcore = config.editor.editorcore;
31052     
31053     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
31054     
31055     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
31056     // dont call parent... till later.
31057 }
31058 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
31059      
31060     bar : true,
31061     
31062     editor : false,
31063     editorcore : false,
31064     
31065     
31066     formats : [
31067         "p" ,  
31068         "h1","h2","h3","h4","h5","h6", 
31069         "pre", "code", 
31070         "abbr", "acronym", "address", "cite", "samp", "var",
31071         'div','span'
31072     ],
31073     
31074     onRender : function(ct, position)
31075     {
31076        // Roo.log("Call onRender: " + this.xtype);
31077         
31078        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
31079        Roo.log(this.el);
31080        this.el.dom.style.marginBottom = '0';
31081        var _this = this;
31082        var editorcore = this.editorcore;
31083        var editor= this.editor;
31084        
31085        var children = [];
31086        var btn = function(id,cmd , toggle, handler, html){
31087        
31088             var  event = toggle ? 'toggle' : 'click';
31089        
31090             var a = {
31091                 size : 'sm',
31092                 xtype: 'Button',
31093                 xns: Roo.bootstrap,
31094                 //glyphicon : id,
31095                 fa: id,
31096                 cmd : id || cmd,
31097                 enableToggle:toggle !== false,
31098                 html : html || '',
31099                 pressed : toggle ? false : null,
31100                 listeners : {}
31101             };
31102             a.listeners[toggle ? 'toggle' : 'click'] = function() {
31103                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
31104             };
31105             children.push(a);
31106             return a;
31107        }
31108        
31109     //    var cb_box = function...
31110         
31111         var style = {
31112                 xtype: 'Button',
31113                 size : 'sm',
31114                 xns: Roo.bootstrap,
31115                 fa : 'font',
31116                 //html : 'submit'
31117                 menu : {
31118                     xtype: 'Menu',
31119                     xns: Roo.bootstrap,
31120                     items:  []
31121                 }
31122         };
31123         Roo.each(this.formats, function(f) {
31124             style.menu.items.push({
31125                 xtype :'MenuItem',
31126                 xns: Roo.bootstrap,
31127                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
31128                 tagname : f,
31129                 listeners : {
31130                     click : function()
31131                     {
31132                         editorcore.insertTag(this.tagname);
31133                         editor.focus();
31134                     }
31135                 }
31136                 
31137             });
31138         });
31139         children.push(style);   
31140         
31141         btn('bold',false,true);
31142         btn('italic',false,true);
31143         btn('align-left', 'justifyleft',true);
31144         btn('align-center', 'justifycenter',true);
31145         btn('align-right' , 'justifyright',true);
31146         btn('link', false, false, function(btn) {
31147             //Roo.log("create link?");
31148             var url = prompt(this.createLinkText, this.defaultLinkValue);
31149             if(url && url != 'http:/'+'/'){
31150                 this.editorcore.relayCmd('createlink', url);
31151             }
31152         }),
31153         btn('list','insertunorderedlist',true);
31154         btn('pencil', false,true, function(btn){
31155                 Roo.log(this);
31156                 this.toggleSourceEdit(btn.pressed);
31157         });
31158         
31159         if (this.editor.btns.length > 0) {
31160             for (var i = 0; i<this.editor.btns.length; i++) {
31161                 children.push(this.editor.btns[i]);
31162             }
31163         }
31164         
31165         /*
31166         var cog = {
31167                 xtype: 'Button',
31168                 size : 'sm',
31169                 xns: Roo.bootstrap,
31170                 glyphicon : 'cog',
31171                 //html : 'submit'
31172                 menu : {
31173                     xtype: 'Menu',
31174                     xns: Roo.bootstrap,
31175                     items:  []
31176                 }
31177         };
31178         
31179         cog.menu.items.push({
31180             xtype :'MenuItem',
31181             xns: Roo.bootstrap,
31182             html : Clean styles,
31183             tagname : f,
31184             listeners : {
31185                 click : function()
31186                 {
31187                     editorcore.insertTag(this.tagname);
31188                     editor.focus();
31189                 }
31190             }
31191             
31192         });
31193        */
31194         
31195          
31196        this.xtype = 'NavSimplebar';
31197         
31198         for(var i=0;i< children.length;i++) {
31199             
31200             this.buttons.add(this.addxtypeChild(children[i]));
31201             
31202         }
31203         
31204         editor.on('editorevent', this.updateToolbar, this);
31205     },
31206     onBtnClick : function(id)
31207     {
31208        this.editorcore.relayCmd(id);
31209        this.editorcore.focus();
31210     },
31211     
31212     /**
31213      * Protected method that will not generally be called directly. It triggers
31214      * a toolbar update by reading the markup state of the current selection in the editor.
31215      */
31216     updateToolbar: function(){
31217
31218         if(!this.editorcore.activated){
31219             this.editor.onFirstFocus(); // is this neeed?
31220             return;
31221         }
31222
31223         var btns = this.buttons; 
31224         var doc = this.editorcore.doc;
31225         btns.get('bold').setActive(doc.queryCommandState('bold'));
31226         btns.get('italic').setActive(doc.queryCommandState('italic'));
31227         //btns.get('underline').setActive(doc.queryCommandState('underline'));
31228         
31229         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31230         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31231         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31232         
31233         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31234         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31235          /*
31236         
31237         var ans = this.editorcore.getAllAncestors();
31238         if (this.formatCombo) {
31239             
31240             
31241             var store = this.formatCombo.store;
31242             this.formatCombo.setValue("");
31243             for (var i =0; i < ans.length;i++) {
31244                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31245                     // select it..
31246                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31247                     break;
31248                 }
31249             }
31250         }
31251         
31252         
31253         
31254         // hides menus... - so this cant be on a menu...
31255         Roo.bootstrap.MenuMgr.hideAll();
31256         */
31257         Roo.bootstrap.menu.Manager.hideAll();
31258         //this.editorsyncValue();
31259     },
31260     onFirstFocus: function() {
31261         this.buttons.each(function(item){
31262            item.enable();
31263         });
31264     },
31265     toggleSourceEdit : function(sourceEditMode){
31266         
31267           
31268         if(sourceEditMode){
31269             Roo.log("disabling buttons");
31270            this.buttons.each( function(item){
31271                 if(item.cmd != 'pencil'){
31272                     item.disable();
31273                 }
31274             });
31275           
31276         }else{
31277             Roo.log("enabling buttons");
31278             if(this.editorcore.initialized){
31279                 this.buttons.each( function(item){
31280                     item.enable();
31281                 });
31282             }
31283             
31284         }
31285         Roo.log("calling toggole on editor");
31286         // tell the editor that it's been pressed..
31287         this.editor.toggleSourceEdit(sourceEditMode);
31288        
31289     }
31290 });
31291
31292
31293
31294
31295  
31296 /*
31297  * - LGPL
31298  */
31299
31300 /**
31301  * @class Roo.bootstrap.form.Markdown
31302  * @extends Roo.bootstrap.form.TextArea
31303  * Bootstrap Showdown editable area
31304  * @cfg {string} content
31305  * 
31306  * @constructor
31307  * Create a new Showdown
31308  */
31309
31310 Roo.bootstrap.form.Markdown = function(config){
31311     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31312    
31313 };
31314
31315 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
31316     
31317     editing :false,
31318     
31319     initEvents : function()
31320     {
31321         
31322         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31323         this.markdownEl = this.el.createChild({
31324             cls : 'roo-markdown-area'
31325         });
31326         this.inputEl().addClass('d-none');
31327         if (this.getValue() == '') {
31328             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31329             
31330         } else {
31331             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31332         }
31333         this.markdownEl.on('click', this.toggleTextEdit, this);
31334         this.on('blur', this.toggleTextEdit, this);
31335         this.on('specialkey', this.resizeTextArea, this);
31336     },
31337     
31338     toggleTextEdit : function()
31339     {
31340         var sh = this.markdownEl.getHeight();
31341         this.inputEl().addClass('d-none');
31342         this.markdownEl.addClass('d-none');
31343         if (!this.editing) {
31344             // show editor?
31345             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31346             this.inputEl().removeClass('d-none');
31347             this.inputEl().focus();
31348             this.editing = true;
31349             return;
31350         }
31351         // show showdown...
31352         this.updateMarkdown();
31353         this.markdownEl.removeClass('d-none');
31354         this.editing = false;
31355         return;
31356     },
31357     updateMarkdown : function()
31358     {
31359         if (this.getValue() == '') {
31360             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31361             return;
31362         }
31363  
31364         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31365     },
31366     
31367     resizeTextArea: function () {
31368         
31369         var sh = 100;
31370         Roo.log([sh, this.getValue().split("\n").length * 30]);
31371         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31372     },
31373     setValue : function(val)
31374     {
31375         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31376         if (!this.editing) {
31377             this.updateMarkdown();
31378         }
31379         
31380     },
31381     focus : function()
31382     {
31383         if (!this.editing) {
31384             this.toggleTextEdit();
31385         }
31386         
31387     }
31388
31389
31390 });/*
31391  * Based on:
31392  * Ext JS Library 1.1.1
31393  * Copyright(c) 2006-2007, Ext JS, LLC.
31394  *
31395  * Originally Released Under LGPL - original licence link has changed is not relivant.
31396  *
31397  * Fork - LGPL
31398  * <script type="text/javascript">
31399  */
31400  
31401 /**
31402  * @class Roo.bootstrap.PagingToolbar
31403  * @extends Roo.bootstrap.nav.Simplebar
31404  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31405  * @constructor
31406  * Create a new PagingToolbar
31407  * @param {Object} config The config object
31408  * @param {Roo.data.Store} store
31409  */
31410 Roo.bootstrap.PagingToolbar = function(config)
31411 {
31412     // old args format still supported... - xtype is prefered..
31413         // created from xtype...
31414     
31415     this.ds = config.dataSource;
31416     
31417     if (config.store && !this.ds) {
31418         this.store= Roo.factory(config.store, Roo.data);
31419         this.ds = this.store;
31420         this.ds.xmodule = this.xmodule || false;
31421     }
31422     
31423     this.toolbarItems = [];
31424     if (config.items) {
31425         this.toolbarItems = config.items;
31426     }
31427     
31428     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31429     
31430     this.cursor = 0;
31431     
31432     if (this.ds) { 
31433         this.bind(this.ds);
31434     }
31435     
31436     if (Roo.bootstrap.version == 4) {
31437         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31438     } else {
31439         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31440     }
31441     
31442 };
31443
31444 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31445     /**
31446      * @cfg {Roo.bootstrap.Button} buttons[]
31447      * Buttons for the toolbar
31448      */
31449      /**
31450      * @cfg {Roo.data.Store} store
31451      * The underlying data store providing the paged data
31452      */
31453     /**
31454      * @cfg {String/HTMLElement/Element} container
31455      * container The id or element that will contain the toolbar
31456      */
31457     /**
31458      * @cfg {Boolean} displayInfo
31459      * True to display the displayMsg (defaults to false)
31460      */
31461     /**
31462      * @cfg {Number} pageSize
31463      * The number of records to display per page (defaults to 20)
31464      */
31465     pageSize: 20,
31466     /**
31467      * @cfg {String} displayMsg
31468      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31469      */
31470     displayMsg : 'Displaying {0} - {1} of {2}',
31471     /**
31472      * @cfg {String} emptyMsg
31473      * The message to display when no records are found (defaults to "No data to display")
31474      */
31475     emptyMsg : 'No data to display',
31476     /**
31477      * Customizable piece of the default paging text (defaults to "Page")
31478      * @type String
31479      */
31480     beforePageText : "Page",
31481     /**
31482      * Customizable piece of the default paging text (defaults to "of %0")
31483      * @type String
31484      */
31485     afterPageText : "of {0}",
31486     /**
31487      * Customizable piece of the default paging text (defaults to "First Page")
31488      * @type String
31489      */
31490     firstText : "First Page",
31491     /**
31492      * Customizable piece of the default paging text (defaults to "Previous Page")
31493      * @type String
31494      */
31495     prevText : "Previous Page",
31496     /**
31497      * Customizable piece of the default paging text (defaults to "Next Page")
31498      * @type String
31499      */
31500     nextText : "Next Page",
31501     /**
31502      * Customizable piece of the default paging text (defaults to "Last Page")
31503      * @type String
31504      */
31505     lastText : "Last Page",
31506     /**
31507      * Customizable piece of the default paging text (defaults to "Refresh")
31508      * @type String
31509      */
31510     refreshText : "Refresh",
31511
31512     buttons : false,
31513     // private
31514     onRender : function(ct, position) 
31515     {
31516         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31517         this.navgroup.parentId = this.id;
31518         this.navgroup.onRender(this.el, null);
31519         // add the buttons to the navgroup
31520         
31521         if(this.displayInfo){
31522             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31523             this.displayEl = this.el.select('.x-paging-info', true).first();
31524 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31525 //            this.displayEl = navel.el.select('span',true).first();
31526         }
31527         
31528         var _this = this;
31529         
31530         if(this.buttons){
31531             Roo.each(_this.buttons, function(e){ // this might need to use render????
31532                Roo.factory(e).render(_this.el);
31533             });
31534         }
31535             
31536         Roo.each(_this.toolbarItems, function(e) {
31537             _this.navgroup.addItem(e);
31538         });
31539         
31540         
31541         this.first = this.navgroup.addItem({
31542             tooltip: this.firstText,
31543             cls: "prev btn-outline-secondary",
31544             html : ' <i class="fa fa-step-backward"></i>',
31545             disabled: true,
31546             preventDefault: true,
31547             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31548         });
31549         
31550         this.prev =  this.navgroup.addItem({
31551             tooltip: this.prevText,
31552             cls: "prev btn-outline-secondary",
31553             html : ' <i class="fa fa-backward"></i>',
31554             disabled: true,
31555             preventDefault: true,
31556             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
31557         });
31558     //this.addSeparator();
31559         
31560         
31561         var field = this.navgroup.addItem( {
31562             tagtype : 'span',
31563             cls : 'x-paging-position  btn-outline-secondary',
31564              disabled: true,
31565             html : this.beforePageText  +
31566                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31567                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
31568          } ); //?? escaped?
31569         
31570         this.field = field.el.select('input', true).first();
31571         this.field.on("keydown", this.onPagingKeydown, this);
31572         this.field.on("focus", function(){this.dom.select();});
31573     
31574     
31575         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
31576         //this.field.setHeight(18);
31577         //this.addSeparator();
31578         this.next = this.navgroup.addItem({
31579             tooltip: this.nextText,
31580             cls: "next btn-outline-secondary",
31581             html : ' <i class="fa fa-forward"></i>',
31582             disabled: true,
31583             preventDefault: true,
31584             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
31585         });
31586         this.last = this.navgroup.addItem({
31587             tooltip: this.lastText,
31588             html : ' <i class="fa fa-step-forward"></i>',
31589             cls: "next btn-outline-secondary",
31590             disabled: true,
31591             preventDefault: true,
31592             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
31593         });
31594     //this.addSeparator();
31595         this.loading = this.navgroup.addItem({
31596             tooltip: this.refreshText,
31597             cls: "btn-outline-secondary",
31598             html : ' <i class="fa fa-refresh"></i>',
31599             preventDefault: true,
31600             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31601         });
31602         
31603     },
31604
31605     // private
31606     updateInfo : function(){
31607         if(this.displayEl){
31608             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31609             var msg = count == 0 ?
31610                 this.emptyMsg :
31611                 String.format(
31612                     this.displayMsg,
31613                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31614                 );
31615             this.displayEl.update(msg);
31616         }
31617     },
31618
31619     // private
31620     onLoad : function(ds, r, o)
31621     {
31622         this.cursor = o.params && o.params.start ? o.params.start : 0;
31623         
31624         var d = this.getPageData(),
31625             ap = d.activePage,
31626             ps = d.pages;
31627         
31628         
31629         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31630         this.field.dom.value = ap;
31631         this.first.setDisabled(ap == 1);
31632         this.prev.setDisabled(ap == 1);
31633         this.next.setDisabled(ap == ps);
31634         this.last.setDisabled(ap == ps);
31635         this.loading.enable();
31636         this.updateInfo();
31637     },
31638
31639     // private
31640     getPageData : function(){
31641         var total = this.ds.getTotalCount();
31642         return {
31643             total : total,
31644             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31645             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31646         };
31647     },
31648
31649     // private
31650     onLoadError : function(proxy, o){
31651         this.loading.enable();
31652         if (this.ds.events.loadexception.listeners.length  < 2) {
31653             // nothing has been assigned to loadexception except this...
31654             // so 
31655             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31656
31657         }
31658     },
31659
31660     // private
31661     onPagingKeydown : function(e){
31662         var k = e.getKey();
31663         var d = this.getPageData();
31664         if(k == e.RETURN){
31665             var v = this.field.dom.value, pageNum;
31666             if(!v || isNaN(pageNum = parseInt(v, 10))){
31667                 this.field.dom.value = d.activePage;
31668                 return;
31669             }
31670             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31671             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31672             e.stopEvent();
31673         }
31674         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))
31675         {
31676           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31677           this.field.dom.value = pageNum;
31678           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31679           e.stopEvent();
31680         }
31681         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31682         {
31683           var v = this.field.dom.value, pageNum; 
31684           var increment = (e.shiftKey) ? 10 : 1;
31685           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31686                 increment *= -1;
31687           }
31688           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31689             this.field.dom.value = d.activePage;
31690             return;
31691           }
31692           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31693           {
31694             this.field.dom.value = parseInt(v, 10) + increment;
31695             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31696             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31697           }
31698           e.stopEvent();
31699         }
31700     },
31701
31702     // private
31703     beforeLoad : function(){
31704         if(this.loading){
31705             this.loading.disable();
31706         }
31707     },
31708
31709     // private
31710     onClick : function(which){
31711         
31712         var ds = this.ds;
31713         if (!ds) {
31714             return;
31715         }
31716         
31717         switch(which){
31718             case "first":
31719                 ds.load({params:{start: 0, limit: this.pageSize}});
31720             break;
31721             case "prev":
31722                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31723             break;
31724             case "next":
31725                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31726             break;
31727             case "last":
31728                 var total = ds.getTotalCount();
31729                 var extra = total % this.pageSize;
31730                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31731                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31732             break;
31733             case "refresh":
31734                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31735             break;
31736         }
31737     },
31738
31739     /**
31740      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31741      * @param {Roo.data.Store} store The data store to unbind
31742      */
31743     unbind : function(ds){
31744         ds.un("beforeload", this.beforeLoad, this);
31745         ds.un("load", this.onLoad, this);
31746         ds.un("loadexception", this.onLoadError, this);
31747         ds.un("remove", this.updateInfo, this);
31748         ds.un("add", this.updateInfo, this);
31749         this.ds = undefined;
31750     },
31751
31752     /**
31753      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31754      * @param {Roo.data.Store} store The data store to bind
31755      */
31756     bind : function(ds){
31757         ds.on("beforeload", this.beforeLoad, this);
31758         ds.on("load", this.onLoad, this);
31759         ds.on("loadexception", this.onLoadError, this);
31760         ds.on("remove", this.updateInfo, this);
31761         ds.on("add", this.updateInfo, this);
31762         this.ds = ds;
31763     }
31764 });/*
31765  * - LGPL
31766  *
31767  * element
31768  * 
31769  */
31770
31771 /**
31772  * @class Roo.bootstrap.MessageBar
31773  * @extends Roo.bootstrap.Component
31774  * Bootstrap MessageBar class
31775  * @cfg {String} html contents of the MessageBar
31776  * @cfg {String} weight (info | success | warning | danger) default info
31777  * @cfg {String} beforeClass insert the bar before the given class
31778  * @cfg {Boolean} closable (true | false) default false
31779  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31780  * 
31781  * @constructor
31782  * Create a new Element
31783  * @param {Object} config The config object
31784  */
31785
31786 Roo.bootstrap.MessageBar = function(config){
31787     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31788 };
31789
31790 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
31791     
31792     html: '',
31793     weight: 'info',
31794     closable: false,
31795     fixed: false,
31796     beforeClass: 'bootstrap-sticky-wrap',
31797     
31798     getAutoCreate : function(){
31799         
31800         var cfg = {
31801             tag: 'div',
31802             cls: 'alert alert-dismissable alert-' + this.weight,
31803             cn: [
31804                 {
31805                     tag: 'span',
31806                     cls: 'message',
31807                     html: this.html || ''
31808                 }
31809             ]
31810         };
31811         
31812         if(this.fixed){
31813             cfg.cls += ' alert-messages-fixed';
31814         }
31815         
31816         if(this.closable){
31817             cfg.cn.push({
31818                 tag: 'button',
31819                 cls: 'close',
31820                 html: 'x'
31821             });
31822         }
31823         
31824         return cfg;
31825     },
31826     
31827     onRender : function(ct, position)
31828     {
31829         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31830         
31831         if(!this.el){
31832             var cfg = Roo.apply({},  this.getAutoCreate());
31833             cfg.id = Roo.id();
31834             
31835             if (this.cls) {
31836                 cfg.cls += ' ' + this.cls;
31837             }
31838             if (this.style) {
31839                 cfg.style = this.style;
31840             }
31841             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31842             
31843             this.el.setVisibilityMode(Roo.Element.DISPLAY);
31844         }
31845         
31846         this.el.select('>button.close').on('click', this.hide, this);
31847         
31848     },
31849     
31850     show : function()
31851     {
31852         if (!this.rendered) {
31853             this.render();
31854         }
31855         
31856         this.el.show();
31857         
31858         this.fireEvent('show', this);
31859         
31860     },
31861     
31862     hide : function()
31863     {
31864         if (!this.rendered) {
31865             this.render();
31866         }
31867         
31868         this.el.hide();
31869         
31870         this.fireEvent('hide', this);
31871     },
31872     
31873     update : function()
31874     {
31875 //        var e = this.el.dom.firstChild;
31876 //        
31877 //        if(this.closable){
31878 //            e = e.nextSibling;
31879 //        }
31880 //        
31881 //        e.data = this.html || '';
31882
31883         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31884     }
31885    
31886 });
31887
31888  
31889
31890      /*
31891  * - LGPL
31892  *
31893  * Graph
31894  * 
31895  */
31896
31897
31898 /**
31899  * @class Roo.bootstrap.Graph
31900  * @extends Roo.bootstrap.Component
31901  * Bootstrap Graph class
31902 > Prameters
31903  -sm {number} sm 4
31904  -md {number} md 5
31905  @cfg {String} graphtype  bar | vbar | pie
31906  @cfg {number} g_x coodinator | centre x (pie)
31907  @cfg {number} g_y coodinator | centre y (pie)
31908  @cfg {number} g_r radius (pie)
31909  @cfg {number} g_height height of the chart (respected by all elements in the set)
31910  @cfg {number} g_width width of the chart (respected by all elements in the set)
31911  @cfg {Object} title The title of the chart
31912     
31913  -{Array}  values
31914  -opts (object) options for the chart 
31915      o {
31916      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31917      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31918      o vgutter (number)
31919      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.
31920      o stacked (boolean) whether or not to tread values as in a stacked bar chart
31921      o to
31922      o stretch (boolean)
31923      o }
31924  -opts (object) options for the pie
31925      o{
31926      o cut
31927      o startAngle (number)
31928      o endAngle (number)
31929      } 
31930  *
31931  * @constructor
31932  * Create a new Input
31933  * @param {Object} config The config object
31934  */
31935
31936 Roo.bootstrap.Graph = function(config){
31937     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31938     
31939     this.addEvents({
31940         // img events
31941         /**
31942          * @event click
31943          * The img click event for the img.
31944          * @param {Roo.EventObject} e
31945          */
31946         "click" : true
31947     });
31948 };
31949
31950 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
31951     
31952     sm: 4,
31953     md: 5,
31954     graphtype: 'bar',
31955     g_height: 250,
31956     g_width: 400,
31957     g_x: 50,
31958     g_y: 50,
31959     g_r: 30,
31960     opts:{
31961         //g_colors: this.colors,
31962         g_type: 'soft',
31963         g_gutter: '20%'
31964
31965     },
31966     title : false,
31967
31968     getAutoCreate : function(){
31969         
31970         var cfg = {
31971             tag: 'div',
31972             html : null
31973         };
31974         
31975         
31976         return  cfg;
31977     },
31978
31979     onRender : function(ct,position){
31980         
31981         
31982         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31983         
31984         if (typeof(Raphael) == 'undefined') {
31985             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31986             return;
31987         }
31988         
31989         this.raphael = Raphael(this.el.dom);
31990         
31991                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31992                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31993                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31994                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31995                 /*
31996                 r.text(160, 10, "Single Series Chart").attr(txtattr);
31997                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31998                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31999                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
32000                 
32001                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
32002                 r.barchart(330, 10, 300, 220, data1);
32003                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
32004                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
32005                 */
32006                 
32007                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32008                 // r.barchart(30, 30, 560, 250,  xdata, {
32009                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
32010                 //     axis : "0 0 1 1",
32011                 //     axisxlabels :  xdata
32012                 //     //yvalues : cols,
32013                    
32014                 // });
32015 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32016 //        
32017 //        this.load(null,xdata,{
32018 //                axis : "0 0 1 1",
32019 //                axisxlabels :  xdata
32020 //                });
32021
32022     },
32023
32024     load : function(graphtype,xdata,opts)
32025     {
32026         this.raphael.clear();
32027         if(!graphtype) {
32028             graphtype = this.graphtype;
32029         }
32030         if(!opts){
32031             opts = this.opts;
32032         }
32033         var r = this.raphael,
32034             fin = function () {
32035                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
32036             },
32037             fout = function () {
32038                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
32039             },
32040             pfin = function() {
32041                 this.sector.stop();
32042                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
32043
32044                 if (this.label) {
32045                     this.label[0].stop();
32046                     this.label[0].attr({ r: 7.5 });
32047                     this.label[1].attr({ "font-weight": 800 });
32048                 }
32049             },
32050             pfout = function() {
32051                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
32052
32053                 if (this.label) {
32054                     this.label[0].animate({ r: 5 }, 500, "bounce");
32055                     this.label[1].attr({ "font-weight": 400 });
32056                 }
32057             };
32058
32059         switch(graphtype){
32060             case 'bar':
32061                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32062                 break;
32063             case 'hbar':
32064                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32065                 break;
32066             case 'pie':
32067 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
32068 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
32069 //            
32070                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
32071                 
32072                 break;
32073
32074         }
32075         
32076         if(this.title){
32077             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
32078         }
32079         
32080     },
32081     
32082     setTitle: function(o)
32083     {
32084         this.title = o;
32085     },
32086     
32087     initEvents: function() {
32088         
32089         if(!this.href){
32090             this.el.on('click', this.onClick, this);
32091         }
32092     },
32093     
32094     onClick : function(e)
32095     {
32096         Roo.log('img onclick');
32097         this.fireEvent('click', this, e);
32098     }
32099    
32100 });
32101
32102  
32103 Roo.bootstrap.dash = {};/*
32104  * - LGPL
32105  *
32106  * numberBox
32107  * 
32108  */
32109 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32110
32111 /**
32112  * @class Roo.bootstrap.dash.NumberBox
32113  * @extends Roo.bootstrap.Component
32114  * Bootstrap NumberBox class
32115  * @cfg {String} headline Box headline
32116  * @cfg {String} content Box content
32117  * @cfg {String} icon Box icon
32118  * @cfg {String} footer Footer text
32119  * @cfg {String} fhref Footer href
32120  * 
32121  * @constructor
32122  * Create a new NumberBox
32123  * @param {Object} config The config object
32124  */
32125
32126
32127 Roo.bootstrap.dash.NumberBox = function(config){
32128     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
32129     
32130 };
32131
32132 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
32133     
32134     headline : '',
32135     content : '',
32136     icon : '',
32137     footer : '',
32138     fhref : '',
32139     ficon : '',
32140     
32141     getAutoCreate : function(){
32142         
32143         var cfg = {
32144             tag : 'div',
32145             cls : 'small-box ',
32146             cn : [
32147                 {
32148                     tag : 'div',
32149                     cls : 'inner',
32150                     cn :[
32151                         {
32152                             tag : 'h3',
32153                             cls : 'roo-headline',
32154                             html : this.headline
32155                         },
32156                         {
32157                             tag : 'p',
32158                             cls : 'roo-content',
32159                             html : this.content
32160                         }
32161                     ]
32162                 }
32163             ]
32164         };
32165         
32166         if(this.icon){
32167             cfg.cn.push({
32168                 tag : 'div',
32169                 cls : 'icon',
32170                 cn :[
32171                     {
32172                         tag : 'i',
32173                         cls : 'ion ' + this.icon
32174                     }
32175                 ]
32176             });
32177         }
32178         
32179         if(this.footer){
32180             var footer = {
32181                 tag : 'a',
32182                 cls : 'small-box-footer',
32183                 href : this.fhref || '#',
32184                 html : this.footer
32185             };
32186             
32187             cfg.cn.push(footer);
32188             
32189         }
32190         
32191         return  cfg;
32192     },
32193
32194     onRender : function(ct,position){
32195         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32196
32197
32198        
32199                 
32200     },
32201
32202     setHeadline: function (value)
32203     {
32204         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32205     },
32206     
32207     setFooter: function (value, href)
32208     {
32209         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32210         
32211         if(href){
32212             this.el.select('a.small-box-footer',true).first().attr('href', href);
32213         }
32214         
32215     },
32216
32217     setContent: function (value)
32218     {
32219         this.el.select('.roo-content',true).first().dom.innerHTML = value;
32220     },
32221
32222     initEvents: function() 
32223     {   
32224         
32225     }
32226     
32227 });
32228
32229  
32230 /*
32231  * - LGPL
32232  *
32233  * TabBox
32234  * 
32235  */
32236 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32237
32238 /**
32239  * @class Roo.bootstrap.dash.TabBox
32240  * @extends Roo.bootstrap.Component
32241  * @children Roo.bootstrap.dash.TabPane
32242  * Bootstrap TabBox class
32243  * @cfg {String} title Title of the TabBox
32244  * @cfg {String} icon Icon of the TabBox
32245  * @cfg {Boolean} showtabs (true|false) show the tabs default true
32246  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32247  * 
32248  * @constructor
32249  * Create a new TabBox
32250  * @param {Object} config The config object
32251  */
32252
32253
32254 Roo.bootstrap.dash.TabBox = function(config){
32255     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32256     this.addEvents({
32257         // raw events
32258         /**
32259          * @event addpane
32260          * When a pane is added
32261          * @param {Roo.bootstrap.dash.TabPane} pane
32262          */
32263         "addpane" : true,
32264         /**
32265          * @event activatepane
32266          * When a pane is activated
32267          * @param {Roo.bootstrap.dash.TabPane} pane
32268          */
32269         "activatepane" : true
32270         
32271          
32272     });
32273     
32274     this.panes = [];
32275 };
32276
32277 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
32278
32279     title : '',
32280     icon : false,
32281     showtabs : true,
32282     tabScrollable : false,
32283     
32284     getChildContainer : function()
32285     {
32286         return this.el.select('.tab-content', true).first();
32287     },
32288     
32289     getAutoCreate : function(){
32290         
32291         var header = {
32292             tag: 'li',
32293             cls: 'pull-left header',
32294             html: this.title,
32295             cn : []
32296         };
32297         
32298         if(this.icon){
32299             header.cn.push({
32300                 tag: 'i',
32301                 cls: 'fa ' + this.icon
32302             });
32303         }
32304         
32305         var h = {
32306             tag: 'ul',
32307             cls: 'nav nav-tabs pull-right',
32308             cn: [
32309                 header
32310             ]
32311         };
32312         
32313         if(this.tabScrollable){
32314             h = {
32315                 tag: 'div',
32316                 cls: 'tab-header',
32317                 cn: [
32318                     {
32319                         tag: 'ul',
32320                         cls: 'nav nav-tabs pull-right',
32321                         cn: [
32322                             header
32323                         ]
32324                     }
32325                 ]
32326             };
32327         }
32328         
32329         var cfg = {
32330             tag: 'div',
32331             cls: 'nav-tabs-custom',
32332             cn: [
32333                 h,
32334                 {
32335                     tag: 'div',
32336                     cls: 'tab-content no-padding',
32337                     cn: []
32338                 }
32339             ]
32340         };
32341
32342         return  cfg;
32343     },
32344     initEvents : function()
32345     {
32346         //Roo.log('add add pane handler');
32347         this.on('addpane', this.onAddPane, this);
32348     },
32349      /**
32350      * Updates the box title
32351      * @param {String} html to set the title to.
32352      */
32353     setTitle : function(value)
32354     {
32355         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32356     },
32357     onAddPane : function(pane)
32358     {
32359         this.panes.push(pane);
32360         //Roo.log('addpane');
32361         //Roo.log(pane);
32362         // tabs are rendere left to right..
32363         if(!this.showtabs){
32364             return;
32365         }
32366         
32367         var ctr = this.el.select('.nav-tabs', true).first();
32368          
32369          
32370         var existing = ctr.select('.nav-tab',true);
32371         var qty = existing.getCount();;
32372         
32373         
32374         var tab = ctr.createChild({
32375             tag : 'li',
32376             cls : 'nav-tab' + (qty ? '' : ' active'),
32377             cn : [
32378                 {
32379                     tag : 'a',
32380                     href:'#',
32381                     html : pane.title
32382                 }
32383             ]
32384         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32385         pane.tab = tab;
32386         
32387         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32388         if (!qty) {
32389             pane.el.addClass('active');
32390         }
32391         
32392                 
32393     },
32394     onTabClick : function(ev,un,ob,pane)
32395     {
32396         //Roo.log('tab - prev default');
32397         ev.preventDefault();
32398         
32399         
32400         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32401         pane.tab.addClass('active');
32402         //Roo.log(pane.title);
32403         this.getChildContainer().select('.tab-pane',true).removeClass('active');
32404         // technically we should have a deactivate event.. but maybe add later.
32405         // and it should not de-activate the selected tab...
32406         this.fireEvent('activatepane', pane);
32407         pane.el.addClass('active');
32408         pane.fireEvent('activate');
32409         
32410         
32411     },
32412     
32413     getActivePane : function()
32414     {
32415         var r = false;
32416         Roo.each(this.panes, function(p) {
32417             if(p.el.hasClass('active')){
32418                 r = p;
32419                 return false;
32420             }
32421             
32422             return;
32423         });
32424         
32425         return r;
32426     }
32427     
32428     
32429 });
32430
32431  
32432 /*
32433  * - LGPL
32434  *
32435  * Tab pane
32436  * 
32437  */
32438 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32439 /**
32440  * @class Roo.bootstrap.TabPane
32441  * @extends Roo.bootstrap.Component
32442  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
32443  * Bootstrap TabPane class
32444  * @cfg {Boolean} active (false | true) Default false
32445  * @cfg {String} title title of panel
32446
32447  * 
32448  * @constructor
32449  * Create a new TabPane
32450  * @param {Object} config The config object
32451  */
32452
32453 Roo.bootstrap.dash.TabPane = function(config){
32454     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32455     
32456     this.addEvents({
32457         // raw events
32458         /**
32459          * @event activate
32460          * When a pane is activated
32461          * @param {Roo.bootstrap.dash.TabPane} pane
32462          */
32463         "activate" : true
32464          
32465     });
32466 };
32467
32468 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
32469     
32470     active : false,
32471     title : '',
32472     
32473     // the tabBox that this is attached to.
32474     tab : false,
32475      
32476     getAutoCreate : function() 
32477     {
32478         var cfg = {
32479             tag: 'div',
32480             cls: 'tab-pane'
32481         };
32482         
32483         if(this.active){
32484             cfg.cls += ' active';
32485         }
32486         
32487         return cfg;
32488     },
32489     initEvents  : function()
32490     {
32491         //Roo.log('trigger add pane handler');
32492         this.parent().fireEvent('addpane', this)
32493     },
32494     
32495      /**
32496      * Updates the tab title 
32497      * @param {String} html to set the title to.
32498      */
32499     setTitle: function(str)
32500     {
32501         if (!this.tab) {
32502             return;
32503         }
32504         this.title = str;
32505         this.tab.select('a', true).first().dom.innerHTML = str;
32506         
32507     }
32508     
32509     
32510     
32511 });
32512
32513  
32514
32515
32516  /*
32517  * - LGPL
32518  *
32519  * Tooltip
32520  * 
32521  */
32522
32523 /**
32524  * @class Roo.bootstrap.Tooltip
32525  * Bootstrap Tooltip class
32526  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32527  * to determine which dom element triggers the tooltip.
32528  * 
32529  * It needs to add support for additional attributes like tooltip-position
32530  * 
32531  * @constructor
32532  * Create a new Toolti
32533  * @param {Object} config The config object
32534  */
32535
32536 Roo.bootstrap.Tooltip = function(config){
32537     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32538     
32539     this.alignment = Roo.bootstrap.Tooltip.alignment;
32540     
32541     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32542         this.alignment = config.alignment;
32543     }
32544     
32545 };
32546
32547 Roo.apply(Roo.bootstrap.Tooltip, {
32548     /**
32549      * @function init initialize tooltip monitoring.
32550      * @static
32551      */
32552     currentEl : false,
32553     currentTip : false,
32554     currentRegion : false,
32555     
32556     //  init : delay?
32557     
32558     init : function()
32559     {
32560         Roo.get(document).on('mouseover', this.enter ,this);
32561         Roo.get(document).on('mouseout', this.leave, this);
32562          
32563         
32564         this.currentTip = new Roo.bootstrap.Tooltip();
32565     },
32566     
32567     enter : function(ev)
32568     {
32569         var dom = ev.getTarget();
32570         
32571         //Roo.log(['enter',dom]);
32572         var el = Roo.fly(dom);
32573         if (this.currentEl) {
32574             //Roo.log(dom);
32575             //Roo.log(this.currentEl);
32576             //Roo.log(this.currentEl.contains(dom));
32577             if (this.currentEl == el) {
32578                 return;
32579             }
32580             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32581                 return;
32582             }
32583
32584         }
32585         
32586         if (this.currentTip.el) {
32587             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32588         }    
32589         //Roo.log(ev);
32590         
32591         if(!el || el.dom == document){
32592             return;
32593         }
32594         
32595         var bindEl = el; 
32596         var pel = false;
32597         if (!el.attr('tooltip')) {
32598             pel = el.findParent("[tooltip]");
32599             if (pel) {
32600                 bindEl = Roo.get(pel);
32601             }
32602         }
32603         
32604        
32605         
32606         // you can not look for children, as if el is the body.. then everythign is the child..
32607         if (!pel && !el.attr('tooltip')) { //
32608             if (!el.select("[tooltip]").elements.length) {
32609                 return;
32610             }
32611             // is the mouse over this child...?
32612             bindEl = el.select("[tooltip]").first();
32613             var xy = ev.getXY();
32614             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32615                 //Roo.log("not in region.");
32616                 return;
32617             }
32618             //Roo.log("child element over..");
32619             
32620         }
32621         this.currentEl = el;
32622         this.currentTip.bind(bindEl);
32623         this.currentRegion = Roo.lib.Region.getRegion(dom);
32624         this.currentTip.enter();
32625         
32626     },
32627     leave : function(ev)
32628     {
32629         var dom = ev.getTarget();
32630         //Roo.log(['leave',dom]);
32631         if (!this.currentEl) {
32632             return;
32633         }
32634         
32635         
32636         if (dom != this.currentEl.dom) {
32637             return;
32638         }
32639         var xy = ev.getXY();
32640         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
32641             return;
32642         }
32643         // only activate leave if mouse cursor is outside... bounding box..
32644         
32645         
32646         
32647         
32648         if (this.currentTip) {
32649             this.currentTip.leave();
32650         }
32651         //Roo.log('clear currentEl');
32652         this.currentEl = false;
32653         
32654         
32655     },
32656     alignment : {
32657         'left' : ['r-l', [-2,0], 'right'],
32658         'right' : ['l-r', [2,0], 'left'],
32659         'bottom' : ['t-b', [0,2], 'top'],
32660         'top' : [ 'b-t', [0,-2], 'bottom']
32661     }
32662     
32663 });
32664
32665
32666 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
32667     
32668     
32669     bindEl : false,
32670     
32671     delay : null, // can be { show : 300 , hide: 500}
32672     
32673     timeout : null,
32674     
32675     hoverState : null, //???
32676     
32677     placement : 'bottom', 
32678     
32679     alignment : false,
32680     
32681     getAutoCreate : function(){
32682     
32683         var cfg = {
32684            cls : 'tooltip',   
32685            role : 'tooltip',
32686            cn : [
32687                 {
32688                     cls : 'tooltip-arrow arrow'
32689                 },
32690                 {
32691                     cls : 'tooltip-inner'
32692                 }
32693            ]
32694         };
32695         
32696         return cfg;
32697     },
32698     bind : function(el)
32699     {
32700         this.bindEl = el;
32701     },
32702     
32703     initEvents : function()
32704     {
32705         this.arrowEl = this.el.select('.arrow', true).first();
32706         this.innerEl = this.el.select('.tooltip-inner', true).first();
32707     },
32708     
32709     enter : function () {
32710        
32711         if (this.timeout != null) {
32712             clearTimeout(this.timeout);
32713         }
32714         
32715         this.hoverState = 'in';
32716          //Roo.log("enter - show");
32717         if (!this.delay || !this.delay.show) {
32718             this.show();
32719             return;
32720         }
32721         var _t = this;
32722         this.timeout = setTimeout(function () {
32723             if (_t.hoverState == 'in') {
32724                 _t.show();
32725             }
32726         }, this.delay.show);
32727     },
32728     leave : function()
32729     {
32730         clearTimeout(this.timeout);
32731     
32732         this.hoverState = 'out';
32733          if (!this.delay || !this.delay.hide) {
32734             this.hide();
32735             return;
32736         }
32737        
32738         var _t = this;
32739         this.timeout = setTimeout(function () {
32740             //Roo.log("leave - timeout");
32741             
32742             if (_t.hoverState == 'out') {
32743                 _t.hide();
32744                 Roo.bootstrap.Tooltip.currentEl = false;
32745             }
32746         }, delay);
32747     },
32748     
32749     show : function (msg)
32750     {
32751         if (!this.el) {
32752             this.render(document.body);
32753         }
32754         // set content.
32755         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32756         
32757         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32758         
32759         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32760         
32761         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32762                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32763         
32764         var placement = typeof this.placement == 'function' ?
32765             this.placement.call(this, this.el, on_el) :
32766             this.placement;
32767             
32768         var autoToken = /\s?auto?\s?/i;
32769         var autoPlace = autoToken.test(placement);
32770         if (autoPlace) {
32771             placement = placement.replace(autoToken, '') || 'top';
32772         }
32773         
32774         //this.el.detach()
32775         //this.el.setXY([0,0]);
32776         this.el.show();
32777         //this.el.dom.style.display='block';
32778         
32779         //this.el.appendTo(on_el);
32780         
32781         var p = this.getPosition();
32782         var box = this.el.getBox();
32783         
32784         if (autoPlace) {
32785             // fixme..
32786         }
32787         
32788         var align = this.alignment[placement];
32789         
32790         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32791         
32792         if(placement == 'top' || placement == 'bottom'){
32793             if(xy[0] < 0){
32794                 placement = 'right';
32795             }
32796             
32797             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32798                 placement = 'left';
32799             }
32800             
32801             var scroll = Roo.select('body', true).first().getScroll();
32802             
32803             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32804                 placement = 'top';
32805             }
32806             
32807             align = this.alignment[placement];
32808             
32809             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32810             
32811         }
32812         
32813         var elems = document.getElementsByTagName('div');
32814         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32815         for (var i = 0; i < elems.length; i++) {
32816           var zindex = Number.parseInt(
32817                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32818                 10
32819           );
32820           if (zindex > highest) {
32821             highest = zindex;
32822           }
32823         }
32824         
32825         
32826         
32827         this.el.dom.style.zIndex = highest;
32828         
32829         this.el.alignTo(this.bindEl, align[0],align[1]);
32830         //var arrow = this.el.select('.arrow',true).first();
32831         //arrow.set(align[2], 
32832         
32833         this.el.addClass(placement);
32834         this.el.addClass("bs-tooltip-"+ placement);
32835         
32836         this.el.addClass('in fade show');
32837         
32838         this.hoverState = null;
32839         
32840         if (this.el.hasClass('fade')) {
32841             // fade it?
32842         }
32843         
32844         
32845         
32846         
32847         
32848     },
32849     hide : function()
32850     {
32851          
32852         if (!this.el) {
32853             return;
32854         }
32855         //this.el.setXY([0,0]);
32856         this.el.removeClass(['show', 'in']);
32857         //this.el.hide();
32858         
32859     }
32860     
32861 });
32862  
32863
32864  /*
32865  * - LGPL
32866  *
32867  * Location Picker
32868  * 
32869  */
32870
32871 /**
32872  * @class Roo.bootstrap.LocationPicker
32873  * @extends Roo.bootstrap.Component
32874  * Bootstrap LocationPicker class
32875  * @cfg {Number} latitude Position when init default 0
32876  * @cfg {Number} longitude Position when init default 0
32877  * @cfg {Number} zoom default 15
32878  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32879  * @cfg {Boolean} mapTypeControl default false
32880  * @cfg {Boolean} disableDoubleClickZoom default false
32881  * @cfg {Boolean} scrollwheel default true
32882  * @cfg {Boolean} streetViewControl default false
32883  * @cfg {Number} radius default 0
32884  * @cfg {String} locationName
32885  * @cfg {Boolean} draggable default true
32886  * @cfg {Boolean} enableAutocomplete default false
32887  * @cfg {Boolean} enableReverseGeocode default true
32888  * @cfg {String} markerTitle
32889  * 
32890  * @constructor
32891  * Create a new LocationPicker
32892  * @param {Object} config The config object
32893  */
32894
32895
32896 Roo.bootstrap.LocationPicker = function(config){
32897     
32898     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32899     
32900     this.addEvents({
32901         /**
32902          * @event initial
32903          * Fires when the picker initialized.
32904          * @param {Roo.bootstrap.LocationPicker} this
32905          * @param {Google Location} location
32906          */
32907         initial : true,
32908         /**
32909          * @event positionchanged
32910          * Fires when the picker position changed.
32911          * @param {Roo.bootstrap.LocationPicker} this
32912          * @param {Google Location} location
32913          */
32914         positionchanged : true,
32915         /**
32916          * @event resize
32917          * Fires when the map resize.
32918          * @param {Roo.bootstrap.LocationPicker} this
32919          */
32920         resize : true,
32921         /**
32922          * @event show
32923          * Fires when the map show.
32924          * @param {Roo.bootstrap.LocationPicker} this
32925          */
32926         show : true,
32927         /**
32928          * @event hide
32929          * Fires when the map hide.
32930          * @param {Roo.bootstrap.LocationPicker} this
32931          */
32932         hide : true,
32933         /**
32934          * @event mapClick
32935          * Fires when click the map.
32936          * @param {Roo.bootstrap.LocationPicker} this
32937          * @param {Map event} e
32938          */
32939         mapClick : true,
32940         /**
32941          * @event mapRightClick
32942          * Fires when right click the map.
32943          * @param {Roo.bootstrap.LocationPicker} this
32944          * @param {Map event} e
32945          */
32946         mapRightClick : true,
32947         /**
32948          * @event markerClick
32949          * Fires when click the marker.
32950          * @param {Roo.bootstrap.LocationPicker} this
32951          * @param {Map event} e
32952          */
32953         markerClick : true,
32954         /**
32955          * @event markerRightClick
32956          * Fires when right click the marker.
32957          * @param {Roo.bootstrap.LocationPicker} this
32958          * @param {Map event} e
32959          */
32960         markerRightClick : true,
32961         /**
32962          * @event OverlayViewDraw
32963          * Fires when OverlayView Draw
32964          * @param {Roo.bootstrap.LocationPicker} this
32965          */
32966         OverlayViewDraw : true,
32967         /**
32968          * @event OverlayViewOnAdd
32969          * Fires when OverlayView Draw
32970          * @param {Roo.bootstrap.LocationPicker} this
32971          */
32972         OverlayViewOnAdd : true,
32973         /**
32974          * @event OverlayViewOnRemove
32975          * Fires when OverlayView Draw
32976          * @param {Roo.bootstrap.LocationPicker} this
32977          */
32978         OverlayViewOnRemove : true,
32979         /**
32980          * @event OverlayViewShow
32981          * Fires when OverlayView Draw
32982          * @param {Roo.bootstrap.LocationPicker} this
32983          * @param {Pixel} cpx
32984          */
32985         OverlayViewShow : true,
32986         /**
32987          * @event OverlayViewHide
32988          * Fires when OverlayView Draw
32989          * @param {Roo.bootstrap.LocationPicker} this
32990          */
32991         OverlayViewHide : true,
32992         /**
32993          * @event loadexception
32994          * Fires when load google lib failed.
32995          * @param {Roo.bootstrap.LocationPicker} this
32996          */
32997         loadexception : true
32998     });
32999         
33000 };
33001
33002 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
33003     
33004     gMapContext: false,
33005     
33006     latitude: 0,
33007     longitude: 0,
33008     zoom: 15,
33009     mapTypeId: false,
33010     mapTypeControl: false,
33011     disableDoubleClickZoom: false,
33012     scrollwheel: true,
33013     streetViewControl: false,
33014     radius: 0,
33015     locationName: '',
33016     draggable: true,
33017     enableAutocomplete: false,
33018     enableReverseGeocode: true,
33019     markerTitle: '',
33020     
33021     getAutoCreate: function()
33022     {
33023
33024         var cfg = {
33025             tag: 'div',
33026             cls: 'roo-location-picker'
33027         };
33028         
33029         return cfg
33030     },
33031     
33032     initEvents: function(ct, position)
33033     {       
33034         if(!this.el.getWidth() || this.isApplied()){
33035             return;
33036         }
33037         
33038         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33039         
33040         this.initial();
33041     },
33042     
33043     initial: function()
33044     {
33045         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33046             this.fireEvent('loadexception', this);
33047             return;
33048         }
33049         
33050         if(!this.mapTypeId){
33051             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33052         }
33053         
33054         this.gMapContext = this.GMapContext();
33055         
33056         this.initOverlayView();
33057         
33058         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33059         
33060         var _this = this;
33061                 
33062         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33063             _this.setPosition(_this.gMapContext.marker.position);
33064         });
33065         
33066         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33067             _this.fireEvent('mapClick', this, event);
33068             
33069         });
33070
33071         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33072             _this.fireEvent('mapRightClick', this, event);
33073             
33074         });
33075         
33076         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33077             _this.fireEvent('markerClick', this, event);
33078             
33079         });
33080
33081         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33082             _this.fireEvent('markerRightClick', this, event);
33083             
33084         });
33085         
33086         this.setPosition(this.gMapContext.location);
33087         
33088         this.fireEvent('initial', this, this.gMapContext.location);
33089     },
33090     
33091     initOverlayView: function()
33092     {
33093         var _this = this;
33094         
33095         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33096             
33097             draw: function()
33098             {
33099                 _this.fireEvent('OverlayViewDraw', _this);
33100             },
33101             
33102             onAdd: function()
33103             {
33104                 _this.fireEvent('OverlayViewOnAdd', _this);
33105             },
33106             
33107             onRemove: function()
33108             {
33109                 _this.fireEvent('OverlayViewOnRemove', _this);
33110             },
33111             
33112             show: function(cpx)
33113             {
33114                 _this.fireEvent('OverlayViewShow', _this, cpx);
33115             },
33116             
33117             hide: function()
33118             {
33119                 _this.fireEvent('OverlayViewHide', _this);
33120             }
33121             
33122         });
33123     },
33124     
33125     fromLatLngToContainerPixel: function(event)
33126     {
33127         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33128     },
33129     
33130     isApplied: function() 
33131     {
33132         return this.getGmapContext() == false ? false : true;
33133     },
33134     
33135     getGmapContext: function() 
33136     {
33137         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33138     },
33139     
33140     GMapContext: function() 
33141     {
33142         var position = new google.maps.LatLng(this.latitude, this.longitude);
33143         
33144         var _map = new google.maps.Map(this.el.dom, {
33145             center: position,
33146             zoom: this.zoom,
33147             mapTypeId: this.mapTypeId,
33148             mapTypeControl: this.mapTypeControl,
33149             disableDoubleClickZoom: this.disableDoubleClickZoom,
33150             scrollwheel: this.scrollwheel,
33151             streetViewControl: this.streetViewControl,
33152             locationName: this.locationName,
33153             draggable: this.draggable,
33154             enableAutocomplete: this.enableAutocomplete,
33155             enableReverseGeocode: this.enableReverseGeocode
33156         });
33157         
33158         var _marker = new google.maps.Marker({
33159             position: position,
33160             map: _map,
33161             title: this.markerTitle,
33162             draggable: this.draggable
33163         });
33164         
33165         return {
33166             map: _map,
33167             marker: _marker,
33168             circle: null,
33169             location: position,
33170             radius: this.radius,
33171             locationName: this.locationName,
33172             addressComponents: {
33173                 formatted_address: null,
33174                 addressLine1: null,
33175                 addressLine2: null,
33176                 streetName: null,
33177                 streetNumber: null,
33178                 city: null,
33179                 district: null,
33180                 state: null,
33181                 stateOrProvince: null
33182             },
33183             settings: this,
33184             domContainer: this.el.dom,
33185             geodecoder: new google.maps.Geocoder()
33186         };
33187     },
33188     
33189     drawCircle: function(center, radius, options) 
33190     {
33191         if (this.gMapContext.circle != null) {
33192             this.gMapContext.circle.setMap(null);
33193         }
33194         if (radius > 0) {
33195             radius *= 1;
33196             options = Roo.apply({}, options, {
33197                 strokeColor: "#0000FF",
33198                 strokeOpacity: .35,
33199                 strokeWeight: 2,
33200                 fillColor: "#0000FF",
33201                 fillOpacity: .2
33202             });
33203             
33204             options.map = this.gMapContext.map;
33205             options.radius = radius;
33206             options.center = center;
33207             this.gMapContext.circle = new google.maps.Circle(options);
33208             return this.gMapContext.circle;
33209         }
33210         
33211         return null;
33212     },
33213     
33214     setPosition: function(location) 
33215     {
33216         this.gMapContext.location = location;
33217         this.gMapContext.marker.setPosition(location);
33218         this.gMapContext.map.panTo(location);
33219         this.drawCircle(location, this.gMapContext.radius, {});
33220         
33221         var _this = this;
33222         
33223         if (this.gMapContext.settings.enableReverseGeocode) {
33224             this.gMapContext.geodecoder.geocode({
33225                 latLng: this.gMapContext.location
33226             }, function(results, status) {
33227                 
33228                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33229                     _this.gMapContext.locationName = results[0].formatted_address;
33230                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33231                     
33232                     _this.fireEvent('positionchanged', this, location);
33233                 }
33234             });
33235             
33236             return;
33237         }
33238         
33239         this.fireEvent('positionchanged', this, location);
33240     },
33241     
33242     resize: function()
33243     {
33244         google.maps.event.trigger(this.gMapContext.map, "resize");
33245         
33246         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33247         
33248         this.fireEvent('resize', this);
33249     },
33250     
33251     setPositionByLatLng: function(latitude, longitude)
33252     {
33253         this.setPosition(new google.maps.LatLng(latitude, longitude));
33254     },
33255     
33256     getCurrentPosition: function() 
33257     {
33258         return {
33259             latitude: this.gMapContext.location.lat(),
33260             longitude: this.gMapContext.location.lng()
33261         };
33262     },
33263     
33264     getAddressName: function() 
33265     {
33266         return this.gMapContext.locationName;
33267     },
33268     
33269     getAddressComponents: function() 
33270     {
33271         return this.gMapContext.addressComponents;
33272     },
33273     
33274     address_component_from_google_geocode: function(address_components) 
33275     {
33276         var result = {};
33277         
33278         for (var i = 0; i < address_components.length; i++) {
33279             var component = address_components[i];
33280             if (component.types.indexOf("postal_code") >= 0) {
33281                 result.postalCode = component.short_name;
33282             } else if (component.types.indexOf("street_number") >= 0) {
33283                 result.streetNumber = component.short_name;
33284             } else if (component.types.indexOf("route") >= 0) {
33285                 result.streetName = component.short_name;
33286             } else if (component.types.indexOf("neighborhood") >= 0) {
33287                 result.city = component.short_name;
33288             } else if (component.types.indexOf("locality") >= 0) {
33289                 result.city = component.short_name;
33290             } else if (component.types.indexOf("sublocality") >= 0) {
33291                 result.district = component.short_name;
33292             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33293                 result.stateOrProvince = component.short_name;
33294             } else if (component.types.indexOf("country") >= 0) {
33295                 result.country = component.short_name;
33296             }
33297         }
33298         
33299         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33300         result.addressLine2 = "";
33301         return result;
33302     },
33303     
33304     setZoomLevel: function(zoom)
33305     {
33306         this.gMapContext.map.setZoom(zoom);
33307     },
33308     
33309     show: function()
33310     {
33311         if(!this.el){
33312             return;
33313         }
33314         
33315         this.el.show();
33316         
33317         this.resize();
33318         
33319         this.fireEvent('show', this);
33320     },
33321     
33322     hide: function()
33323     {
33324         if(!this.el){
33325             return;
33326         }
33327         
33328         this.el.hide();
33329         
33330         this.fireEvent('hide', this);
33331     }
33332     
33333 });
33334
33335 Roo.apply(Roo.bootstrap.LocationPicker, {
33336     
33337     OverlayView : function(map, options)
33338     {
33339         options = options || {};
33340         
33341         this.setMap(map);
33342     }
33343     
33344     
33345 });/**
33346  * @class Roo.bootstrap.Alert
33347  * @extends Roo.bootstrap.Component
33348  * Bootstrap Alert class - shows an alert area box
33349  * eg
33350  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33351   Enter a valid email address
33352 </div>
33353  * @licence LGPL
33354  * @cfg {String} title The title of alert
33355  * @cfg {String} html The content of alert
33356  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33357  * @cfg {String} fa font-awesomeicon
33358  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33359  * @cfg {Boolean} close true to show a x closer
33360  * 
33361  * 
33362  * @constructor
33363  * Create a new alert
33364  * @param {Object} config The config object
33365  */
33366
33367
33368 Roo.bootstrap.Alert = function(config){
33369     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33370     
33371 };
33372
33373 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33374     
33375     title: '',
33376     html: '',
33377     weight: false,
33378     fa: false,
33379     faicon: false, // BC
33380     close : false,
33381     
33382     
33383     getAutoCreate : function()
33384     {
33385         
33386         var cfg = {
33387             tag : 'div',
33388             cls : 'alert',
33389             cn : [
33390                 {
33391                     tag: 'button',
33392                     type :  "button",
33393                     cls: "close",
33394                     html : '×',
33395                     style : this.close ? '' : 'display:none'
33396                 },
33397                 {
33398                     tag : 'i',
33399                     cls : 'roo-alert-icon'
33400                     
33401                 },
33402                 {
33403                     tag : 'b',
33404                     cls : 'roo-alert-title',
33405                     html : this.title
33406                 },
33407                 {
33408                     tag : 'span',
33409                     cls : 'roo-alert-text',
33410                     html : this.html
33411                 }
33412             ]
33413         };
33414         
33415         if(this.faicon){
33416             cfg.cn[0].cls += ' fa ' + this.faicon;
33417         }
33418         if(this.fa){
33419             cfg.cn[0].cls += ' fa ' + this.fa;
33420         }
33421         
33422         if(this.weight){
33423             cfg.cls += ' alert-' + this.weight;
33424         }
33425         
33426         return cfg;
33427     },
33428     
33429     initEvents: function() 
33430     {
33431         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33432         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33433         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33434         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33435         if (this.seconds > 0) {
33436             this.hide.defer(this.seconds, this);
33437         }
33438     },
33439     /**
33440      * Set the Title Message HTML
33441      * @param {String} html
33442      */
33443     setTitle : function(str)
33444     {
33445         this.titleEl.dom.innerHTML = str;
33446     },
33447      
33448      /**
33449      * Set the Body Message HTML
33450      * @param {String} html
33451      */
33452     setHtml : function(str)
33453     {
33454         this.htmlEl.dom.innerHTML = str;
33455     },
33456     /**
33457      * Set the Weight of the alert
33458      * @param {String} (success|info|warning|danger) weight
33459      */
33460     
33461     setWeight : function(weight)
33462     {
33463         if(this.weight){
33464             this.el.removeClass('alert-' + this.weight);
33465         }
33466         
33467         this.weight = weight;
33468         
33469         this.el.addClass('alert-' + this.weight);
33470     },
33471       /**
33472      * Set the Icon of the alert
33473      * @param {String} see fontawsome names (name without the 'fa-' bit)
33474      */
33475     setIcon : function(icon)
33476     {
33477         if(this.faicon){
33478             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33479         }
33480         
33481         this.faicon = icon;
33482         
33483         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33484     },
33485     /**
33486      * Hide the Alert
33487      */
33488     hide: function() 
33489     {
33490         this.el.hide();   
33491     },
33492     /**
33493      * Show the Alert
33494      */
33495     show: function() 
33496     {  
33497         this.el.show();   
33498     }
33499     
33500 });
33501
33502  
33503 /*
33504 * Licence: LGPL
33505 */
33506
33507 /**
33508  * @class Roo.bootstrap.UploadCropbox
33509  * @extends Roo.bootstrap.Component
33510  * Bootstrap UploadCropbox class
33511  * @cfg {String} emptyText show when image has been loaded
33512  * @cfg {String} rotateNotify show when image too small to rotate
33513  * @cfg {Number} errorTimeout default 3000
33514  * @cfg {Number} minWidth default 300
33515  * @cfg {Number} minHeight default 300
33516  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33517  * @cfg {Boolean} isDocument (true|false) default false
33518  * @cfg {String} url action url
33519  * @cfg {String} paramName default 'imageUpload'
33520  * @cfg {String} method default POST
33521  * @cfg {Boolean} loadMask (true|false) default true
33522  * @cfg {Boolean} loadingText default 'Loading...'
33523  * 
33524  * @constructor
33525  * Create a new UploadCropbox
33526  * @param {Object} config The config object
33527  */
33528
33529 Roo.bootstrap.UploadCropbox = function(config){
33530     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33531     
33532     this.addEvents({
33533         /**
33534          * @event beforeselectfile
33535          * Fire before select file
33536          * @param {Roo.bootstrap.UploadCropbox} this
33537          */
33538         "beforeselectfile" : true,
33539         /**
33540          * @event initial
33541          * Fire after initEvent
33542          * @param {Roo.bootstrap.UploadCropbox} this
33543          */
33544         "initial" : true,
33545         /**
33546          * @event crop
33547          * Fire after initEvent
33548          * @param {Roo.bootstrap.UploadCropbox} this
33549          * @param {String} data
33550          */
33551         "crop" : true,
33552         /**
33553          * @event prepare
33554          * Fire when preparing the file data
33555          * @param {Roo.bootstrap.UploadCropbox} this
33556          * @param {Object} file
33557          */
33558         "prepare" : true,
33559         /**
33560          * @event exception
33561          * Fire when get exception
33562          * @param {Roo.bootstrap.UploadCropbox} this
33563          * @param {XMLHttpRequest} xhr
33564          */
33565         "exception" : true,
33566         /**
33567          * @event beforeloadcanvas
33568          * Fire before load the canvas
33569          * @param {Roo.bootstrap.UploadCropbox} this
33570          * @param {String} src
33571          */
33572         "beforeloadcanvas" : true,
33573         /**
33574          * @event trash
33575          * Fire when trash image
33576          * @param {Roo.bootstrap.UploadCropbox} this
33577          */
33578         "trash" : true,
33579         /**
33580          * @event download
33581          * Fire when download the image
33582          * @param {Roo.bootstrap.UploadCropbox} this
33583          */
33584         "download" : true,
33585         /**
33586          * @event footerbuttonclick
33587          * Fire when footerbuttonclick
33588          * @param {Roo.bootstrap.UploadCropbox} this
33589          * @param {String} type
33590          */
33591         "footerbuttonclick" : true,
33592         /**
33593          * @event resize
33594          * Fire when resize
33595          * @param {Roo.bootstrap.UploadCropbox} this
33596          */
33597         "resize" : true,
33598         /**
33599          * @event rotate
33600          * Fire when rotate the image
33601          * @param {Roo.bootstrap.UploadCropbox} this
33602          * @param {String} pos
33603          */
33604         "rotate" : true,
33605         /**
33606          * @event inspect
33607          * Fire when inspect the file
33608          * @param {Roo.bootstrap.UploadCropbox} this
33609          * @param {Object} file
33610          */
33611         "inspect" : true,
33612         /**
33613          * @event upload
33614          * Fire when xhr upload the file
33615          * @param {Roo.bootstrap.UploadCropbox} this
33616          * @param {Object} data
33617          */
33618         "upload" : true,
33619         /**
33620          * @event arrange
33621          * Fire when arrange the file data
33622          * @param {Roo.bootstrap.UploadCropbox} this
33623          * @param {Object} formData
33624          */
33625         "arrange" : true
33626     });
33627     
33628     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33629 };
33630
33631 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33632     
33633     emptyText : 'Click to upload image',
33634     rotateNotify : 'Image is too small to rotate',
33635     errorTimeout : 3000,
33636     scale : 0,
33637     baseScale : 1,
33638     rotate : 0,
33639     dragable : false,
33640     pinching : false,
33641     mouseX : 0,
33642     mouseY : 0,
33643     cropData : false,
33644     minWidth : 300,
33645     minHeight : 300,
33646     file : false,
33647     exif : {},
33648     baseRotate : 1,
33649     cropType : 'image/jpeg',
33650     buttons : false,
33651     canvasLoaded : false,
33652     isDocument : false,
33653     method : 'POST',
33654     paramName : 'imageUpload',
33655     loadMask : true,
33656     loadingText : 'Loading...',
33657     maskEl : false,
33658     
33659     getAutoCreate : function()
33660     {
33661         var cfg = {
33662             tag : 'div',
33663             cls : 'roo-upload-cropbox',
33664             cn : [
33665                 {
33666                     tag : 'input',
33667                     cls : 'roo-upload-cropbox-selector',
33668                     type : 'file'
33669                 },
33670                 {
33671                     tag : 'div',
33672                     cls : 'roo-upload-cropbox-body',
33673                     style : 'cursor:pointer',
33674                     cn : [
33675                         {
33676                             tag : 'div',
33677                             cls : 'roo-upload-cropbox-preview'
33678                         },
33679                         {
33680                             tag : 'div',
33681                             cls : 'roo-upload-cropbox-thumb'
33682                         },
33683                         {
33684                             tag : 'div',
33685                             cls : 'roo-upload-cropbox-empty-notify',
33686                             html : this.emptyText
33687                         },
33688                         {
33689                             tag : 'div',
33690                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33691                             html : this.rotateNotify
33692                         }
33693                     ]
33694                 },
33695                 {
33696                     tag : 'div',
33697                     cls : 'roo-upload-cropbox-footer',
33698                     cn : {
33699                         tag : 'div',
33700                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33701                         cn : []
33702                     }
33703                 }
33704             ]
33705         };
33706         
33707         return cfg;
33708     },
33709     
33710     onRender : function(ct, position)
33711     {
33712         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33713         
33714         if (this.buttons.length) {
33715             
33716             Roo.each(this.buttons, function(bb) {
33717                 
33718                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33719                 
33720                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33721                 
33722             }, this);
33723         }
33724         
33725         if(this.loadMask){
33726             this.maskEl = this.el;
33727         }
33728     },
33729     
33730     initEvents : function()
33731     {
33732         this.urlAPI = (window.createObjectURL && window) || 
33733                                 (window.URL && URL.revokeObjectURL && URL) || 
33734                                 (window.webkitURL && webkitURL);
33735                         
33736         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33737         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33738         
33739         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33740         this.selectorEl.hide();
33741         
33742         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33743         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33744         
33745         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33746         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33747         this.thumbEl.hide();
33748         
33749         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33750         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33751         
33752         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33753         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33754         this.errorEl.hide();
33755         
33756         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33757         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33758         this.footerEl.hide();
33759         
33760         this.setThumbBoxSize();
33761         
33762         this.bind();
33763         
33764         this.resize();
33765         
33766         this.fireEvent('initial', this);
33767     },
33768
33769     bind : function()
33770     {
33771         var _this = this;
33772         
33773         window.addEventListener("resize", function() { _this.resize(); } );
33774         
33775         this.bodyEl.on('click', this.beforeSelectFile, this);
33776         
33777         if(Roo.isTouch){
33778             this.bodyEl.on('touchstart', this.onTouchStart, this);
33779             this.bodyEl.on('touchmove', this.onTouchMove, this);
33780             this.bodyEl.on('touchend', this.onTouchEnd, this);
33781         }
33782         
33783         if(!Roo.isTouch){
33784             this.bodyEl.on('mousedown', this.onMouseDown, this);
33785             this.bodyEl.on('mousemove', this.onMouseMove, this);
33786             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33787             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33788             Roo.get(document).on('mouseup', this.onMouseUp, this);
33789         }
33790         
33791         this.selectorEl.on('change', this.onFileSelected, this);
33792     },
33793     
33794     reset : function()
33795     {    
33796         this.scale = 0;
33797         this.baseScale = 1;
33798         this.rotate = 0;
33799         this.baseRotate = 1;
33800         this.dragable = false;
33801         this.pinching = false;
33802         this.mouseX = 0;
33803         this.mouseY = 0;
33804         this.cropData = false;
33805         this.notifyEl.dom.innerHTML = this.emptyText;
33806         
33807         this.selectorEl.dom.value = '';
33808         
33809     },
33810     
33811     resize : function()
33812     {
33813         if(this.fireEvent('resize', this) != false){
33814             this.setThumbBoxPosition();
33815             this.setCanvasPosition();
33816         }
33817     },
33818     
33819     onFooterButtonClick : function(e, el, o, type)
33820     {
33821         switch (type) {
33822             case 'rotate-left' :
33823                 this.onRotateLeft(e);
33824                 break;
33825             case 'rotate-right' :
33826                 this.onRotateRight(e);
33827                 break;
33828             case 'picture' :
33829                 this.beforeSelectFile(e);
33830                 break;
33831             case 'trash' :
33832                 this.trash(e);
33833                 break;
33834             case 'crop' :
33835                 this.crop(e);
33836                 break;
33837             case 'download' :
33838                 this.download(e);
33839                 break;
33840             default :
33841                 break;
33842         }
33843         
33844         this.fireEvent('footerbuttonclick', this, type);
33845     },
33846     
33847     beforeSelectFile : function(e)
33848     {
33849         e.preventDefault();
33850         
33851         if(this.fireEvent('beforeselectfile', this) != false){
33852             this.selectorEl.dom.click();
33853         }
33854     },
33855     
33856     onFileSelected : function(e)
33857     {
33858         e.preventDefault();
33859         
33860         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33861             return;
33862         }
33863         
33864         var file = this.selectorEl.dom.files[0];
33865         
33866         if(this.fireEvent('inspect', this, file) != false){
33867             this.prepare(file);
33868         }
33869         
33870     },
33871     
33872     trash : function(e)
33873     {
33874         this.fireEvent('trash', this);
33875     },
33876     
33877     download : function(e)
33878     {
33879         this.fireEvent('download', this);
33880     },
33881     
33882     loadCanvas : function(src)
33883     {   
33884         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33885             
33886             this.reset();
33887             
33888             this.imageEl = document.createElement('img');
33889             
33890             var _this = this;
33891             
33892             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33893             
33894             this.imageEl.src = src;
33895         }
33896     },
33897     
33898     onLoadCanvas : function()
33899     {   
33900         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33901         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33902         
33903         this.bodyEl.un('click', this.beforeSelectFile, this);
33904         
33905         this.notifyEl.hide();
33906         this.thumbEl.show();
33907         this.footerEl.show();
33908         
33909         this.baseRotateLevel();
33910         
33911         if(this.isDocument){
33912             this.setThumbBoxSize();
33913         }
33914         
33915         this.setThumbBoxPosition();
33916         
33917         this.baseScaleLevel();
33918         
33919         this.draw();
33920         
33921         this.resize();
33922         
33923         this.canvasLoaded = true;
33924         
33925         if(this.loadMask){
33926             this.maskEl.unmask();
33927         }
33928         
33929     },
33930     
33931     setCanvasPosition : function()
33932     {   
33933         if(!this.canvasEl){
33934             return;
33935         }
33936         
33937         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33938         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33939         
33940         this.previewEl.setLeft(pw);
33941         this.previewEl.setTop(ph);
33942         
33943     },
33944     
33945     onMouseDown : function(e)
33946     {   
33947         e.stopEvent();
33948         
33949         this.dragable = true;
33950         this.pinching = false;
33951         
33952         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33953             this.dragable = false;
33954             return;
33955         }
33956         
33957         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33958         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33959         
33960     },
33961     
33962     onMouseMove : function(e)
33963     {   
33964         e.stopEvent();
33965         
33966         if(!this.canvasLoaded){
33967             return;
33968         }
33969         
33970         if (!this.dragable){
33971             return;
33972         }
33973         
33974         var minX = Math.ceil(this.thumbEl.getLeft(true));
33975         var minY = Math.ceil(this.thumbEl.getTop(true));
33976         
33977         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33978         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33979         
33980         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33981         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33982         
33983         x = x - this.mouseX;
33984         y = y - this.mouseY;
33985         
33986         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33987         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33988         
33989         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33990         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33991         
33992         this.previewEl.setLeft(bgX);
33993         this.previewEl.setTop(bgY);
33994         
33995         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33996         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33997     },
33998     
33999     onMouseUp : function(e)
34000     {   
34001         e.stopEvent();
34002         
34003         this.dragable = false;
34004     },
34005     
34006     onMouseWheel : function(e)
34007     {   
34008         e.stopEvent();
34009         
34010         this.startScale = this.scale;
34011         
34012         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34013         
34014         if(!this.zoomable()){
34015             this.scale = this.startScale;
34016             return;
34017         }
34018         
34019         this.draw();
34020         
34021         return;
34022     },
34023     
34024     zoomable : function()
34025     {
34026         var minScale = this.thumbEl.getWidth() / this.minWidth;
34027         
34028         if(this.minWidth < this.minHeight){
34029             minScale = this.thumbEl.getHeight() / this.minHeight;
34030         }
34031         
34032         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34033         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34034         
34035         if(
34036                 this.isDocument &&
34037                 (this.rotate == 0 || this.rotate == 180) && 
34038                 (
34039                     width > this.imageEl.OriginWidth || 
34040                     height > this.imageEl.OriginHeight ||
34041                     (width < this.minWidth && height < this.minHeight)
34042                 )
34043         ){
34044             return false;
34045         }
34046         
34047         if(
34048                 this.isDocument &&
34049                 (this.rotate == 90 || this.rotate == 270) && 
34050                 (
34051                     width > this.imageEl.OriginWidth || 
34052                     height > this.imageEl.OriginHeight ||
34053                     (width < this.minHeight && height < this.minWidth)
34054                 )
34055         ){
34056             return false;
34057         }
34058         
34059         if(
34060                 !this.isDocument &&
34061                 (this.rotate == 0 || this.rotate == 180) && 
34062                 (
34063                     width < this.minWidth || 
34064                     width > this.imageEl.OriginWidth || 
34065                     height < this.minHeight || 
34066                     height > this.imageEl.OriginHeight
34067                 )
34068         ){
34069             return false;
34070         }
34071         
34072         if(
34073                 !this.isDocument &&
34074                 (this.rotate == 90 || this.rotate == 270) && 
34075                 (
34076                     width < this.minHeight || 
34077                     width > this.imageEl.OriginWidth || 
34078                     height < this.minWidth || 
34079                     height > this.imageEl.OriginHeight
34080                 )
34081         ){
34082             return false;
34083         }
34084         
34085         return true;
34086         
34087     },
34088     
34089     onRotateLeft : function(e)
34090     {   
34091         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34092             
34093             var minScale = this.thumbEl.getWidth() / this.minWidth;
34094             
34095             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34096             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34097             
34098             this.startScale = this.scale;
34099             
34100             while (this.getScaleLevel() < minScale){
34101             
34102                 this.scale = this.scale + 1;
34103                 
34104                 if(!this.zoomable()){
34105                     break;
34106                 }
34107                 
34108                 if(
34109                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34110                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34111                 ){
34112                     continue;
34113                 }
34114                 
34115                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34116
34117                 this.draw();
34118                 
34119                 return;
34120             }
34121             
34122             this.scale = this.startScale;
34123             
34124             this.onRotateFail();
34125             
34126             return false;
34127         }
34128         
34129         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34130
34131         if(this.isDocument){
34132             this.setThumbBoxSize();
34133             this.setThumbBoxPosition();
34134             this.setCanvasPosition();
34135         }
34136         
34137         this.draw();
34138         
34139         this.fireEvent('rotate', this, 'left');
34140         
34141     },
34142     
34143     onRotateRight : function(e)
34144     {
34145         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34146             
34147             var minScale = this.thumbEl.getWidth() / this.minWidth;
34148         
34149             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34150             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34151             
34152             this.startScale = this.scale;
34153             
34154             while (this.getScaleLevel() < minScale){
34155             
34156                 this.scale = this.scale + 1;
34157                 
34158                 if(!this.zoomable()){
34159                     break;
34160                 }
34161                 
34162                 if(
34163                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34164                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34165                 ){
34166                     continue;
34167                 }
34168                 
34169                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34170
34171                 this.draw();
34172                 
34173                 return;
34174             }
34175             
34176             this.scale = this.startScale;
34177             
34178             this.onRotateFail();
34179             
34180             return false;
34181         }
34182         
34183         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34184
34185         if(this.isDocument){
34186             this.setThumbBoxSize();
34187             this.setThumbBoxPosition();
34188             this.setCanvasPosition();
34189         }
34190         
34191         this.draw();
34192         
34193         this.fireEvent('rotate', this, 'right');
34194     },
34195     
34196     onRotateFail : function()
34197     {
34198         this.errorEl.show(true);
34199         
34200         var _this = this;
34201         
34202         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34203     },
34204     
34205     draw : function()
34206     {
34207         this.previewEl.dom.innerHTML = '';
34208         
34209         var canvasEl = document.createElement("canvas");
34210         
34211         var contextEl = canvasEl.getContext("2d");
34212         
34213         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34214         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34215         var center = this.imageEl.OriginWidth / 2;
34216         
34217         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34218             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34219             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34220             center = this.imageEl.OriginHeight / 2;
34221         }
34222         
34223         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34224         
34225         contextEl.translate(center, center);
34226         contextEl.rotate(this.rotate * Math.PI / 180);
34227
34228         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34229         
34230         this.canvasEl = document.createElement("canvas");
34231         
34232         this.contextEl = this.canvasEl.getContext("2d");
34233         
34234         switch (this.rotate) {
34235             case 0 :
34236                 
34237                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34238                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34239                 
34240                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34241                 
34242                 break;
34243             case 90 : 
34244                 
34245                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34246                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34247                 
34248                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34249                     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);
34250                     break;
34251                 }
34252                 
34253                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34254                 
34255                 break;
34256             case 180 :
34257                 
34258                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34259                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34260                 
34261                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34262                     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);
34263                     break;
34264                 }
34265                 
34266                 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);
34267                 
34268                 break;
34269             case 270 :
34270                 
34271                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34272                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34273         
34274                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34275                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34276                     break;
34277                 }
34278                 
34279                 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);
34280                 
34281                 break;
34282             default : 
34283                 break;
34284         }
34285         
34286         this.previewEl.appendChild(this.canvasEl);
34287         
34288         this.setCanvasPosition();
34289     },
34290     
34291     crop : function()
34292     {
34293         if(!this.canvasLoaded){
34294             return;
34295         }
34296         
34297         var imageCanvas = document.createElement("canvas");
34298         
34299         var imageContext = imageCanvas.getContext("2d");
34300         
34301         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34302         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34303         
34304         var center = imageCanvas.width / 2;
34305         
34306         imageContext.translate(center, center);
34307         
34308         imageContext.rotate(this.rotate * Math.PI / 180);
34309         
34310         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34311         
34312         var canvas = document.createElement("canvas");
34313         
34314         var context = canvas.getContext("2d");
34315                 
34316         canvas.width = this.minWidth;
34317         canvas.height = this.minHeight;
34318
34319         switch (this.rotate) {
34320             case 0 :
34321                 
34322                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34323                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34324                 
34325                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34326                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34327                 
34328                 var targetWidth = this.minWidth - 2 * x;
34329                 var targetHeight = this.minHeight - 2 * y;
34330                 
34331                 var scale = 1;
34332                 
34333                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34334                     scale = targetWidth / width;
34335                 }
34336                 
34337                 if(x > 0 && y == 0){
34338                     scale = targetHeight / height;
34339                 }
34340                 
34341                 if(x > 0 && y > 0){
34342                     scale = targetWidth / width;
34343                     
34344                     if(width < height){
34345                         scale = targetHeight / height;
34346                     }
34347                 }
34348                 
34349                 context.scale(scale, scale);
34350                 
34351                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34352                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34353
34354                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34355                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34356
34357                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34358                 
34359                 break;
34360             case 90 : 
34361                 
34362                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34363                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34364                 
34365                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34366                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34367                 
34368                 var targetWidth = this.minWidth - 2 * x;
34369                 var targetHeight = this.minHeight - 2 * y;
34370                 
34371                 var scale = 1;
34372                 
34373                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34374                     scale = targetWidth / width;
34375                 }
34376                 
34377                 if(x > 0 && y == 0){
34378                     scale = targetHeight / height;
34379                 }
34380                 
34381                 if(x > 0 && y > 0){
34382                     scale = targetWidth / width;
34383                     
34384                     if(width < height){
34385                         scale = targetHeight / height;
34386                     }
34387                 }
34388                 
34389                 context.scale(scale, scale);
34390                 
34391                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34392                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34393
34394                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34395                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34396                 
34397                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34398                 
34399                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34400                 
34401                 break;
34402             case 180 :
34403                 
34404                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34405                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34406                 
34407                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34408                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34409                 
34410                 var targetWidth = this.minWidth - 2 * x;
34411                 var targetHeight = this.minHeight - 2 * y;
34412                 
34413                 var scale = 1;
34414                 
34415                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34416                     scale = targetWidth / width;
34417                 }
34418                 
34419                 if(x > 0 && y == 0){
34420                     scale = targetHeight / height;
34421                 }
34422                 
34423                 if(x > 0 && y > 0){
34424                     scale = targetWidth / width;
34425                     
34426                     if(width < height){
34427                         scale = targetHeight / height;
34428                     }
34429                 }
34430                 
34431                 context.scale(scale, scale);
34432                 
34433                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34434                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34435
34436                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34437                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34438
34439                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34440                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34441                 
34442                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34443                 
34444                 break;
34445             case 270 :
34446                 
34447                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34448                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34449                 
34450                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34451                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34452                 
34453                 var targetWidth = this.minWidth - 2 * x;
34454                 var targetHeight = this.minHeight - 2 * y;
34455                 
34456                 var scale = 1;
34457                 
34458                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34459                     scale = targetWidth / width;
34460                 }
34461                 
34462                 if(x > 0 && y == 0){
34463                     scale = targetHeight / height;
34464                 }
34465                 
34466                 if(x > 0 && y > 0){
34467                     scale = targetWidth / width;
34468                     
34469                     if(width < height){
34470                         scale = targetHeight / height;
34471                     }
34472                 }
34473                 
34474                 context.scale(scale, scale);
34475                 
34476                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34477                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34478
34479                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34480                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34481                 
34482                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34483                 
34484                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34485                 
34486                 break;
34487             default : 
34488                 break;
34489         }
34490         
34491         this.cropData = canvas.toDataURL(this.cropType);
34492         
34493         if(this.fireEvent('crop', this, this.cropData) !== false){
34494             this.process(this.file, this.cropData);
34495         }
34496         
34497         return;
34498         
34499     },
34500     
34501     setThumbBoxSize : function()
34502     {
34503         var width, height;
34504         
34505         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34506             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34507             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34508             
34509             this.minWidth = width;
34510             this.minHeight = height;
34511             
34512             if(this.rotate == 90 || this.rotate == 270){
34513                 this.minWidth = height;
34514                 this.minHeight = width;
34515             }
34516         }
34517         
34518         height = 300;
34519         width = Math.ceil(this.minWidth * height / this.minHeight);
34520         
34521         if(this.minWidth > this.minHeight){
34522             width = 300;
34523             height = Math.ceil(this.minHeight * width / this.minWidth);
34524         }
34525         
34526         this.thumbEl.setStyle({
34527             width : width + 'px',
34528             height : height + 'px'
34529         });
34530
34531         return;
34532             
34533     },
34534     
34535     setThumbBoxPosition : function()
34536     {
34537         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34538         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34539         
34540         this.thumbEl.setLeft(x);
34541         this.thumbEl.setTop(y);
34542         
34543     },
34544     
34545     baseRotateLevel : function()
34546     {
34547         this.baseRotate = 1;
34548         
34549         if(
34550                 typeof(this.exif) != 'undefined' &&
34551                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34552                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34553         ){
34554             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34555         }
34556         
34557         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34558         
34559     },
34560     
34561     baseScaleLevel : function()
34562     {
34563         var width, height;
34564         
34565         if(this.isDocument){
34566             
34567             if(this.baseRotate == 6 || this.baseRotate == 8){
34568             
34569                 height = this.thumbEl.getHeight();
34570                 this.baseScale = height / this.imageEl.OriginWidth;
34571
34572                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34573                     width = this.thumbEl.getWidth();
34574                     this.baseScale = width / this.imageEl.OriginHeight;
34575                 }
34576
34577                 return;
34578             }
34579
34580             height = this.thumbEl.getHeight();
34581             this.baseScale = height / this.imageEl.OriginHeight;
34582
34583             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34584                 width = this.thumbEl.getWidth();
34585                 this.baseScale = width / this.imageEl.OriginWidth;
34586             }
34587
34588             return;
34589         }
34590         
34591         if(this.baseRotate == 6 || this.baseRotate == 8){
34592             
34593             width = this.thumbEl.getHeight();
34594             this.baseScale = width / this.imageEl.OriginHeight;
34595             
34596             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34597                 height = this.thumbEl.getWidth();
34598                 this.baseScale = height / this.imageEl.OriginHeight;
34599             }
34600             
34601             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34602                 height = this.thumbEl.getWidth();
34603                 this.baseScale = height / this.imageEl.OriginHeight;
34604                 
34605                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34606                     width = this.thumbEl.getHeight();
34607                     this.baseScale = width / this.imageEl.OriginWidth;
34608                 }
34609             }
34610             
34611             return;
34612         }
34613         
34614         width = this.thumbEl.getWidth();
34615         this.baseScale = width / this.imageEl.OriginWidth;
34616         
34617         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34618             height = this.thumbEl.getHeight();
34619             this.baseScale = height / this.imageEl.OriginHeight;
34620         }
34621         
34622         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34623             
34624             height = this.thumbEl.getHeight();
34625             this.baseScale = height / this.imageEl.OriginHeight;
34626             
34627             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34628                 width = this.thumbEl.getWidth();
34629                 this.baseScale = width / this.imageEl.OriginWidth;
34630             }
34631             
34632         }
34633         
34634         return;
34635     },
34636     
34637     getScaleLevel : function()
34638     {
34639         return this.baseScale * Math.pow(1.1, this.scale);
34640     },
34641     
34642     onTouchStart : function(e)
34643     {
34644         if(!this.canvasLoaded){
34645             this.beforeSelectFile(e);
34646             return;
34647         }
34648         
34649         var touches = e.browserEvent.touches;
34650         
34651         if(!touches){
34652             return;
34653         }
34654         
34655         if(touches.length == 1){
34656             this.onMouseDown(e);
34657             return;
34658         }
34659         
34660         if(touches.length != 2){
34661             return;
34662         }
34663         
34664         var coords = [];
34665         
34666         for(var i = 0, finger; finger = touches[i]; i++){
34667             coords.push(finger.pageX, finger.pageY);
34668         }
34669         
34670         var x = Math.pow(coords[0] - coords[2], 2);
34671         var y = Math.pow(coords[1] - coords[3], 2);
34672         
34673         this.startDistance = Math.sqrt(x + y);
34674         
34675         this.startScale = this.scale;
34676         
34677         this.pinching = true;
34678         this.dragable = false;
34679         
34680     },
34681     
34682     onTouchMove : function(e)
34683     {
34684         if(!this.pinching && !this.dragable){
34685             return;
34686         }
34687         
34688         var touches = e.browserEvent.touches;
34689         
34690         if(!touches){
34691             return;
34692         }
34693         
34694         if(this.dragable){
34695             this.onMouseMove(e);
34696             return;
34697         }
34698         
34699         var coords = [];
34700         
34701         for(var i = 0, finger; finger = touches[i]; i++){
34702             coords.push(finger.pageX, finger.pageY);
34703         }
34704         
34705         var x = Math.pow(coords[0] - coords[2], 2);
34706         var y = Math.pow(coords[1] - coords[3], 2);
34707         
34708         this.endDistance = Math.sqrt(x + y);
34709         
34710         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34711         
34712         if(!this.zoomable()){
34713             this.scale = this.startScale;
34714             return;
34715         }
34716         
34717         this.draw();
34718         
34719     },
34720     
34721     onTouchEnd : function(e)
34722     {
34723         this.pinching = false;
34724         this.dragable = false;
34725         
34726     },
34727     
34728     process : function(file, crop)
34729     {
34730         if(this.loadMask){
34731             this.maskEl.mask(this.loadingText);
34732         }
34733         
34734         this.xhr = new XMLHttpRequest();
34735         
34736         file.xhr = this.xhr;
34737
34738         this.xhr.open(this.method, this.url, true);
34739         
34740         var headers = {
34741             "Accept": "application/json",
34742             "Cache-Control": "no-cache",
34743             "X-Requested-With": "XMLHttpRequest"
34744         };
34745         
34746         for (var headerName in headers) {
34747             var headerValue = headers[headerName];
34748             if (headerValue) {
34749                 this.xhr.setRequestHeader(headerName, headerValue);
34750             }
34751         }
34752         
34753         var _this = this;
34754         
34755         this.xhr.onload = function()
34756         {
34757             _this.xhrOnLoad(_this.xhr);
34758         }
34759         
34760         this.xhr.onerror = function()
34761         {
34762             _this.xhrOnError(_this.xhr);
34763         }
34764         
34765         var formData = new FormData();
34766
34767         formData.append('returnHTML', 'NO');
34768         
34769         if(crop){
34770             formData.append('crop', crop);
34771         }
34772         
34773         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34774             formData.append(this.paramName, file, file.name);
34775         }
34776         
34777         if(typeof(file.filename) != 'undefined'){
34778             formData.append('filename', file.filename);
34779         }
34780         
34781         if(typeof(file.mimetype) != 'undefined'){
34782             formData.append('mimetype', file.mimetype);
34783         }
34784         
34785         if(this.fireEvent('arrange', this, formData) != false){
34786             this.xhr.send(formData);
34787         };
34788     },
34789     
34790     xhrOnLoad : function(xhr)
34791     {
34792         if(this.loadMask){
34793             this.maskEl.unmask();
34794         }
34795         
34796         if (xhr.readyState !== 4) {
34797             this.fireEvent('exception', this, xhr);
34798             return;
34799         }
34800
34801         var response = Roo.decode(xhr.responseText);
34802         
34803         if(!response.success){
34804             this.fireEvent('exception', this, xhr);
34805             return;
34806         }
34807         
34808         var response = Roo.decode(xhr.responseText);
34809         
34810         this.fireEvent('upload', this, response);
34811         
34812     },
34813     
34814     xhrOnError : function()
34815     {
34816         if(this.loadMask){
34817             this.maskEl.unmask();
34818         }
34819         
34820         Roo.log('xhr on error');
34821         
34822         var response = Roo.decode(xhr.responseText);
34823           
34824         Roo.log(response);
34825         
34826     },
34827     
34828     prepare : function(file)
34829     {   
34830         if(this.loadMask){
34831             this.maskEl.mask(this.loadingText);
34832         }
34833         
34834         this.file = false;
34835         this.exif = {};
34836         
34837         if(typeof(file) === 'string'){
34838             this.loadCanvas(file);
34839             return;
34840         }
34841         
34842         if(!file || !this.urlAPI){
34843             return;
34844         }
34845         
34846         this.file = file;
34847         this.cropType = file.type;
34848         
34849         var _this = this;
34850         
34851         if(this.fireEvent('prepare', this, this.file) != false){
34852             
34853             var reader = new FileReader();
34854             
34855             reader.onload = function (e) {
34856                 if (e.target.error) {
34857                     Roo.log(e.target.error);
34858                     return;
34859                 }
34860                 
34861                 var buffer = e.target.result,
34862                     dataView = new DataView(buffer),
34863                     offset = 2,
34864                     maxOffset = dataView.byteLength - 4,
34865                     markerBytes,
34866                     markerLength;
34867                 
34868                 if (dataView.getUint16(0) === 0xffd8) {
34869                     while (offset < maxOffset) {
34870                         markerBytes = dataView.getUint16(offset);
34871                         
34872                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34873                             markerLength = dataView.getUint16(offset + 2) + 2;
34874                             if (offset + markerLength > dataView.byteLength) {
34875                                 Roo.log('Invalid meta data: Invalid segment size.');
34876                                 break;
34877                             }
34878                             
34879                             if(markerBytes == 0xffe1){
34880                                 _this.parseExifData(
34881                                     dataView,
34882                                     offset,
34883                                     markerLength
34884                                 );
34885                             }
34886                             
34887                             offset += markerLength;
34888                             
34889                             continue;
34890                         }
34891                         
34892                         break;
34893                     }
34894                     
34895                 }
34896                 
34897                 var url = _this.urlAPI.createObjectURL(_this.file);
34898                 
34899                 _this.loadCanvas(url);
34900                 
34901                 return;
34902             }
34903             
34904             reader.readAsArrayBuffer(this.file);
34905             
34906         }
34907         
34908     },
34909     
34910     parseExifData : function(dataView, offset, length)
34911     {
34912         var tiffOffset = offset + 10,
34913             littleEndian,
34914             dirOffset;
34915     
34916         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34917             // No Exif data, might be XMP data instead
34918             return;
34919         }
34920         
34921         // Check for the ASCII code for "Exif" (0x45786966):
34922         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34923             // No Exif data, might be XMP data instead
34924             return;
34925         }
34926         if (tiffOffset + 8 > dataView.byteLength) {
34927             Roo.log('Invalid Exif data: Invalid segment size.');
34928             return;
34929         }
34930         // Check for the two null bytes:
34931         if (dataView.getUint16(offset + 8) !== 0x0000) {
34932             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34933             return;
34934         }
34935         // Check the byte alignment:
34936         switch (dataView.getUint16(tiffOffset)) {
34937         case 0x4949:
34938             littleEndian = true;
34939             break;
34940         case 0x4D4D:
34941             littleEndian = false;
34942             break;
34943         default:
34944             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34945             return;
34946         }
34947         // Check for the TIFF tag marker (0x002A):
34948         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34949             Roo.log('Invalid Exif data: Missing TIFF marker.');
34950             return;
34951         }
34952         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34953         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34954         
34955         this.parseExifTags(
34956             dataView,
34957             tiffOffset,
34958             tiffOffset + dirOffset,
34959             littleEndian
34960         );
34961     },
34962     
34963     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34964     {
34965         var tagsNumber,
34966             dirEndOffset,
34967             i;
34968         if (dirOffset + 6 > dataView.byteLength) {
34969             Roo.log('Invalid Exif data: Invalid directory offset.');
34970             return;
34971         }
34972         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34973         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34974         if (dirEndOffset + 4 > dataView.byteLength) {
34975             Roo.log('Invalid Exif data: Invalid directory size.');
34976             return;
34977         }
34978         for (i = 0; i < tagsNumber; i += 1) {
34979             this.parseExifTag(
34980                 dataView,
34981                 tiffOffset,
34982                 dirOffset + 2 + 12 * i, // tag offset
34983                 littleEndian
34984             );
34985         }
34986         // Return the offset to the next directory:
34987         return dataView.getUint32(dirEndOffset, littleEndian);
34988     },
34989     
34990     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34991     {
34992         var tag = dataView.getUint16(offset, littleEndian);
34993         
34994         this.exif[tag] = this.getExifValue(
34995             dataView,
34996             tiffOffset,
34997             offset,
34998             dataView.getUint16(offset + 2, littleEndian), // tag type
34999             dataView.getUint32(offset + 4, littleEndian), // tag length
35000             littleEndian
35001         );
35002     },
35003     
35004     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35005     {
35006         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35007             tagSize,
35008             dataOffset,
35009             values,
35010             i,
35011             str,
35012             c;
35013     
35014         if (!tagType) {
35015             Roo.log('Invalid Exif data: Invalid tag type.');
35016             return;
35017         }
35018         
35019         tagSize = tagType.size * length;
35020         // Determine if the value is contained in the dataOffset bytes,
35021         // or if the value at the dataOffset is a pointer to the actual data:
35022         dataOffset = tagSize > 4 ?
35023                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35024         if (dataOffset + tagSize > dataView.byteLength) {
35025             Roo.log('Invalid Exif data: Invalid data offset.');
35026             return;
35027         }
35028         if (length === 1) {
35029             return tagType.getValue(dataView, dataOffset, littleEndian);
35030         }
35031         values = [];
35032         for (i = 0; i < length; i += 1) {
35033             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35034         }
35035         
35036         if (tagType.ascii) {
35037             str = '';
35038             // Concatenate the chars:
35039             for (i = 0; i < values.length; i += 1) {
35040                 c = values[i];
35041                 // Ignore the terminating NULL byte(s):
35042                 if (c === '\u0000') {
35043                     break;
35044                 }
35045                 str += c;
35046             }
35047             return str;
35048         }
35049         return values;
35050     }
35051     
35052 });
35053
35054 Roo.apply(Roo.bootstrap.UploadCropbox, {
35055     tags : {
35056         'Orientation': 0x0112
35057     },
35058     
35059     Orientation: {
35060             1: 0, //'top-left',
35061 //            2: 'top-right',
35062             3: 180, //'bottom-right',
35063 //            4: 'bottom-left',
35064 //            5: 'left-top',
35065             6: 90, //'right-top',
35066 //            7: 'right-bottom',
35067             8: 270 //'left-bottom'
35068     },
35069     
35070     exifTagTypes : {
35071         // byte, 8-bit unsigned int:
35072         1: {
35073             getValue: function (dataView, dataOffset) {
35074                 return dataView.getUint8(dataOffset);
35075             },
35076             size: 1
35077         },
35078         // ascii, 8-bit byte:
35079         2: {
35080             getValue: function (dataView, dataOffset) {
35081                 return String.fromCharCode(dataView.getUint8(dataOffset));
35082             },
35083             size: 1,
35084             ascii: true
35085         },
35086         // short, 16 bit int:
35087         3: {
35088             getValue: function (dataView, dataOffset, littleEndian) {
35089                 return dataView.getUint16(dataOffset, littleEndian);
35090             },
35091             size: 2
35092         },
35093         // long, 32 bit int:
35094         4: {
35095             getValue: function (dataView, dataOffset, littleEndian) {
35096                 return dataView.getUint32(dataOffset, littleEndian);
35097             },
35098             size: 4
35099         },
35100         // rational = two long values, first is numerator, second is denominator:
35101         5: {
35102             getValue: function (dataView, dataOffset, littleEndian) {
35103                 return dataView.getUint32(dataOffset, littleEndian) /
35104                     dataView.getUint32(dataOffset + 4, littleEndian);
35105             },
35106             size: 8
35107         },
35108         // slong, 32 bit signed int:
35109         9: {
35110             getValue: function (dataView, dataOffset, littleEndian) {
35111                 return dataView.getInt32(dataOffset, littleEndian);
35112             },
35113             size: 4
35114         },
35115         // srational, two slongs, first is numerator, second is denominator:
35116         10: {
35117             getValue: function (dataView, dataOffset, littleEndian) {
35118                 return dataView.getInt32(dataOffset, littleEndian) /
35119                     dataView.getInt32(dataOffset + 4, littleEndian);
35120             },
35121             size: 8
35122         }
35123     },
35124     
35125     footer : {
35126         STANDARD : [
35127             {
35128                 tag : 'div',
35129                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35130                 action : 'rotate-left',
35131                 cn : [
35132                     {
35133                         tag : 'button',
35134                         cls : 'btn btn-default',
35135                         html : '<i class="fa fa-undo"></i>'
35136                     }
35137                 ]
35138             },
35139             {
35140                 tag : 'div',
35141                 cls : 'btn-group roo-upload-cropbox-picture',
35142                 action : 'picture',
35143                 cn : [
35144                     {
35145                         tag : 'button',
35146                         cls : 'btn btn-default',
35147                         html : '<i class="fa fa-picture-o"></i>'
35148                     }
35149                 ]
35150             },
35151             {
35152                 tag : 'div',
35153                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35154                 action : 'rotate-right',
35155                 cn : [
35156                     {
35157                         tag : 'button',
35158                         cls : 'btn btn-default',
35159                         html : '<i class="fa fa-repeat"></i>'
35160                     }
35161                 ]
35162             }
35163         ],
35164         DOCUMENT : [
35165             {
35166                 tag : 'div',
35167                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35168                 action : 'rotate-left',
35169                 cn : [
35170                     {
35171                         tag : 'button',
35172                         cls : 'btn btn-default',
35173                         html : '<i class="fa fa-undo"></i>'
35174                     }
35175                 ]
35176             },
35177             {
35178                 tag : 'div',
35179                 cls : 'btn-group roo-upload-cropbox-download',
35180                 action : 'download',
35181                 cn : [
35182                     {
35183                         tag : 'button',
35184                         cls : 'btn btn-default',
35185                         html : '<i class="fa fa-download"></i>'
35186                     }
35187                 ]
35188             },
35189             {
35190                 tag : 'div',
35191                 cls : 'btn-group roo-upload-cropbox-crop',
35192                 action : 'crop',
35193                 cn : [
35194                     {
35195                         tag : 'button',
35196                         cls : 'btn btn-default',
35197                         html : '<i class="fa fa-crop"></i>'
35198                     }
35199                 ]
35200             },
35201             {
35202                 tag : 'div',
35203                 cls : 'btn-group roo-upload-cropbox-trash',
35204                 action : 'trash',
35205                 cn : [
35206                     {
35207                         tag : 'button',
35208                         cls : 'btn btn-default',
35209                         html : '<i class="fa fa-trash"></i>'
35210                     }
35211                 ]
35212             },
35213             {
35214                 tag : 'div',
35215                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35216                 action : 'rotate-right',
35217                 cn : [
35218                     {
35219                         tag : 'button',
35220                         cls : 'btn btn-default',
35221                         html : '<i class="fa fa-repeat"></i>'
35222                     }
35223                 ]
35224             }
35225         ],
35226         ROTATOR : [
35227             {
35228                 tag : 'div',
35229                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35230                 action : 'rotate-left',
35231                 cn : [
35232                     {
35233                         tag : 'button',
35234                         cls : 'btn btn-default',
35235                         html : '<i class="fa fa-undo"></i>'
35236                     }
35237                 ]
35238             },
35239             {
35240                 tag : 'div',
35241                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35242                 action : 'rotate-right',
35243                 cn : [
35244                     {
35245                         tag : 'button',
35246                         cls : 'btn btn-default',
35247                         html : '<i class="fa fa-repeat"></i>'
35248                     }
35249                 ]
35250             }
35251         ]
35252     }
35253 });
35254
35255 /*
35256 * Licence: LGPL
35257 */
35258
35259 /**
35260  * @class Roo.bootstrap.DocumentManager
35261  * @extends Roo.bootstrap.Component
35262  * Bootstrap DocumentManager class
35263  * @cfg {String} paramName default 'imageUpload'
35264  * @cfg {String} toolTipName default 'filename'
35265  * @cfg {String} method default POST
35266  * @cfg {String} url action url
35267  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35268  * @cfg {Boolean} multiple multiple upload default true
35269  * @cfg {Number} thumbSize default 300
35270  * @cfg {String} fieldLabel
35271  * @cfg {Number} labelWidth default 4
35272  * @cfg {String} labelAlign (left|top) default left
35273  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35274 * @cfg {Number} labellg set the width of label (1-12)
35275  * @cfg {Number} labelmd set the width of label (1-12)
35276  * @cfg {Number} labelsm set the width of label (1-12)
35277  * @cfg {Number} labelxs set the width of label (1-12)
35278  * 
35279  * @constructor
35280  * Create a new DocumentManager
35281  * @param {Object} config The config object
35282  */
35283
35284 Roo.bootstrap.DocumentManager = function(config){
35285     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35286     
35287     this.files = [];
35288     this.delegates = [];
35289     
35290     this.addEvents({
35291         /**
35292          * @event initial
35293          * Fire when initial the DocumentManager
35294          * @param {Roo.bootstrap.DocumentManager} this
35295          */
35296         "initial" : true,
35297         /**
35298          * @event inspect
35299          * inspect selected file
35300          * @param {Roo.bootstrap.DocumentManager} this
35301          * @param {File} file
35302          */
35303         "inspect" : true,
35304         /**
35305          * @event exception
35306          * Fire when xhr load exception
35307          * @param {Roo.bootstrap.DocumentManager} this
35308          * @param {XMLHttpRequest} xhr
35309          */
35310         "exception" : true,
35311         /**
35312          * @event afterupload
35313          * Fire when xhr load exception
35314          * @param {Roo.bootstrap.DocumentManager} this
35315          * @param {XMLHttpRequest} xhr
35316          */
35317         "afterupload" : true,
35318         /**
35319          * @event prepare
35320          * prepare the form data
35321          * @param {Roo.bootstrap.DocumentManager} this
35322          * @param {Object} formData
35323          */
35324         "prepare" : true,
35325         /**
35326          * @event remove
35327          * Fire when remove the file
35328          * @param {Roo.bootstrap.DocumentManager} this
35329          * @param {Object} file
35330          */
35331         "remove" : true,
35332         /**
35333          * @event refresh
35334          * Fire after refresh the file
35335          * @param {Roo.bootstrap.DocumentManager} this
35336          */
35337         "refresh" : true,
35338         /**
35339          * @event click
35340          * Fire after click the image
35341          * @param {Roo.bootstrap.DocumentManager} this
35342          * @param {Object} file
35343          */
35344         "click" : true,
35345         /**
35346          * @event edit
35347          * Fire when upload a image and editable set to true
35348          * @param {Roo.bootstrap.DocumentManager} this
35349          * @param {Object} file
35350          */
35351         "edit" : true,
35352         /**
35353          * @event beforeselectfile
35354          * Fire before select file
35355          * @param {Roo.bootstrap.DocumentManager} this
35356          */
35357         "beforeselectfile" : true,
35358         /**
35359          * @event process
35360          * Fire before process file
35361          * @param {Roo.bootstrap.DocumentManager} this
35362          * @param {Object} file
35363          */
35364         "process" : true,
35365         /**
35366          * @event previewrendered
35367          * Fire when preview rendered
35368          * @param {Roo.bootstrap.DocumentManager} this
35369          * @param {Object} file
35370          */
35371         "previewrendered" : true,
35372         /**
35373          */
35374         "previewResize" : true
35375         
35376     });
35377 };
35378
35379 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35380     
35381     boxes : 0,
35382     inputName : '',
35383     thumbSize : 300,
35384     multiple : true,
35385     files : false,
35386     method : 'POST',
35387     url : '',
35388     paramName : 'imageUpload',
35389     toolTipName : 'filename',
35390     fieldLabel : '',
35391     labelWidth : 4,
35392     labelAlign : 'left',
35393     editable : true,
35394     delegates : false,
35395     xhr : false, 
35396     
35397     labellg : 0,
35398     labelmd : 0,
35399     labelsm : 0,
35400     labelxs : 0,
35401     
35402     getAutoCreate : function()
35403     {   
35404         var managerWidget = {
35405             tag : 'div',
35406             cls : 'roo-document-manager',
35407             cn : [
35408                 {
35409                     tag : 'input',
35410                     cls : 'roo-document-manager-selector',
35411                     type : 'file'
35412                 },
35413                 {
35414                     tag : 'div',
35415                     cls : 'roo-document-manager-uploader',
35416                     cn : [
35417                         {
35418                             tag : 'div',
35419                             cls : 'roo-document-manager-upload-btn',
35420                             html : '<i class="fa fa-plus"></i>'
35421                         }
35422                     ]
35423                     
35424                 }
35425             ]
35426         };
35427         
35428         var content = [
35429             {
35430                 tag : 'div',
35431                 cls : 'column col-md-12',
35432                 cn : managerWidget
35433             }
35434         ];
35435         
35436         if(this.fieldLabel.length){
35437             
35438             content = [
35439                 {
35440                     tag : 'div',
35441                     cls : 'column col-md-12',
35442                     html : this.fieldLabel
35443                 },
35444                 {
35445                     tag : 'div',
35446                     cls : 'column col-md-12',
35447                     cn : managerWidget
35448                 }
35449             ];
35450
35451             if(this.labelAlign == 'left'){
35452                 content = [
35453                     {
35454                         tag : 'div',
35455                         cls : 'column',
35456                         html : this.fieldLabel
35457                     },
35458                     {
35459                         tag : 'div',
35460                         cls : 'column',
35461                         cn : managerWidget
35462                     }
35463                 ];
35464                 
35465                 if(this.labelWidth > 12){
35466                     content[0].style = "width: " + this.labelWidth + 'px';
35467                 }
35468
35469                 if(this.labelWidth < 13 && this.labelmd == 0){
35470                     this.labelmd = this.labelWidth;
35471                 }
35472
35473                 if(this.labellg > 0){
35474                     content[0].cls += ' col-lg-' + this.labellg;
35475                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35476                 }
35477
35478                 if(this.labelmd > 0){
35479                     content[0].cls += ' col-md-' + this.labelmd;
35480                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35481                 }
35482
35483                 if(this.labelsm > 0){
35484                     content[0].cls += ' col-sm-' + this.labelsm;
35485                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35486                 }
35487
35488                 if(this.labelxs > 0){
35489                     content[0].cls += ' col-xs-' + this.labelxs;
35490                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35491                 }
35492                 
35493             }
35494         }
35495         
35496         var cfg = {
35497             tag : 'div',
35498             cls : 'row clearfix',
35499             cn : content
35500         };
35501         
35502         return cfg;
35503         
35504     },
35505     
35506     initEvents : function()
35507     {
35508         this.managerEl = this.el.select('.roo-document-manager', true).first();
35509         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35510         
35511         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35512         this.selectorEl.hide();
35513         
35514         if(this.multiple){
35515             this.selectorEl.attr('multiple', 'multiple');
35516         }
35517         
35518         this.selectorEl.on('change', this.onFileSelected, this);
35519         
35520         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35521         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35522         
35523         this.uploader.on('click', this.onUploaderClick, this);
35524         
35525         this.renderProgressDialog();
35526         
35527         var _this = this;
35528         
35529         window.addEventListener("resize", function() { _this.refresh(); } );
35530         
35531         this.fireEvent('initial', this);
35532     },
35533     
35534     renderProgressDialog : function()
35535     {
35536         var _this = this;
35537         
35538         this.progressDialog = new Roo.bootstrap.Modal({
35539             cls : 'roo-document-manager-progress-dialog',
35540             allow_close : false,
35541             animate : false,
35542             title : '',
35543             buttons : [
35544                 {
35545                     name  :'cancel',
35546                     weight : 'danger',
35547                     html : 'Cancel'
35548                 }
35549             ], 
35550             listeners : { 
35551                 btnclick : function() {
35552                     _this.uploadCancel();
35553                     this.hide();
35554                 }
35555             }
35556         });
35557          
35558         this.progressDialog.render(Roo.get(document.body));
35559          
35560         this.progress = new Roo.bootstrap.Progress({
35561             cls : 'roo-document-manager-progress',
35562             active : true,
35563             striped : true
35564         });
35565         
35566         this.progress.render(this.progressDialog.getChildContainer());
35567         
35568         this.progressBar = new Roo.bootstrap.ProgressBar({
35569             cls : 'roo-document-manager-progress-bar',
35570             aria_valuenow : 0,
35571             aria_valuemin : 0,
35572             aria_valuemax : 12,
35573             panel : 'success'
35574         });
35575         
35576         this.progressBar.render(this.progress.getChildContainer());
35577     },
35578     
35579     onUploaderClick : function(e)
35580     {
35581         e.preventDefault();
35582      
35583         if(this.fireEvent('beforeselectfile', this) != false){
35584             this.selectorEl.dom.click();
35585         }
35586         
35587     },
35588     
35589     onFileSelected : function(e)
35590     {
35591         e.preventDefault();
35592         
35593         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35594             return;
35595         }
35596         
35597         Roo.each(this.selectorEl.dom.files, function(file){
35598             if(this.fireEvent('inspect', this, file) != false){
35599                 this.files.push(file);
35600             }
35601         }, this);
35602         
35603         this.queue();
35604         
35605     },
35606     
35607     queue : function()
35608     {
35609         this.selectorEl.dom.value = '';
35610         
35611         if(!this.files || !this.files.length){
35612             return;
35613         }
35614         
35615         if(this.boxes > 0 && this.files.length > this.boxes){
35616             this.files = this.files.slice(0, this.boxes);
35617         }
35618         
35619         this.uploader.show();
35620         
35621         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35622             this.uploader.hide();
35623         }
35624         
35625         var _this = this;
35626         
35627         var files = [];
35628         
35629         var docs = [];
35630         
35631         Roo.each(this.files, function(file){
35632             
35633             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35634                 var f = this.renderPreview(file);
35635                 files.push(f);
35636                 return;
35637             }
35638             
35639             if(file.type.indexOf('image') != -1){
35640                 this.delegates.push(
35641                     (function(){
35642                         _this.process(file);
35643                     }).createDelegate(this)
35644                 );
35645         
35646                 return;
35647             }
35648             
35649             docs.push(
35650                 (function(){
35651                     _this.process(file);
35652                 }).createDelegate(this)
35653             );
35654             
35655         }, this);
35656         
35657         this.files = files;
35658         
35659         this.delegates = this.delegates.concat(docs);
35660         
35661         if(!this.delegates.length){
35662             this.refresh();
35663             return;
35664         }
35665         
35666         this.progressBar.aria_valuemax = this.delegates.length;
35667         
35668         this.arrange();
35669         
35670         return;
35671     },
35672     
35673     arrange : function()
35674     {
35675         if(!this.delegates.length){
35676             this.progressDialog.hide();
35677             this.refresh();
35678             return;
35679         }
35680         
35681         var delegate = this.delegates.shift();
35682         
35683         this.progressDialog.show();
35684         
35685         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35686         
35687         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35688         
35689         delegate();
35690     },
35691     
35692     refresh : function()
35693     {
35694         this.uploader.show();
35695         
35696         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35697             this.uploader.hide();
35698         }
35699         
35700         Roo.isTouch ? this.closable(false) : this.closable(true);
35701         
35702         this.fireEvent('refresh', this);
35703     },
35704     
35705     onRemove : function(e, el, o)
35706     {
35707         e.preventDefault();
35708         
35709         this.fireEvent('remove', this, o);
35710         
35711     },
35712     
35713     remove : function(o)
35714     {
35715         var files = [];
35716         
35717         Roo.each(this.files, function(file){
35718             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35719                 files.push(file);
35720                 return;
35721             }
35722
35723             o.target.remove();
35724
35725         }, this);
35726         
35727         this.files = files;
35728         
35729         this.refresh();
35730     },
35731     
35732     clear : function()
35733     {
35734         Roo.each(this.files, function(file){
35735             if(!file.target){
35736                 return;
35737             }
35738             
35739             file.target.remove();
35740
35741         }, this);
35742         
35743         this.files = [];
35744         
35745         this.refresh();
35746     },
35747     
35748     onClick : function(e, el, o)
35749     {
35750         e.preventDefault();
35751         
35752         this.fireEvent('click', this, o);
35753         
35754     },
35755     
35756     closable : function(closable)
35757     {
35758         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35759             
35760             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35761             
35762             if(closable){
35763                 el.show();
35764                 return;
35765             }
35766             
35767             el.hide();
35768             
35769         }, this);
35770     },
35771     
35772     xhrOnLoad : function(xhr)
35773     {
35774         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35775             el.remove();
35776         }, this);
35777         
35778         if (xhr.readyState !== 4) {
35779             this.arrange();
35780             this.fireEvent('exception', this, xhr);
35781             return;
35782         }
35783
35784         var response = Roo.decode(xhr.responseText);
35785         
35786         if(!response.success){
35787             this.arrange();
35788             this.fireEvent('exception', this, xhr);
35789             return;
35790         }
35791         
35792         var file = this.renderPreview(response.data);
35793         
35794         this.files.push(file);
35795         
35796         this.arrange();
35797         
35798         this.fireEvent('afterupload', this, xhr);
35799         
35800     },
35801     
35802     xhrOnError : function(xhr)
35803     {
35804         Roo.log('xhr on error');
35805         
35806         var response = Roo.decode(xhr.responseText);
35807           
35808         Roo.log(response);
35809         
35810         this.arrange();
35811     },
35812     
35813     process : function(file)
35814     {
35815         if(this.fireEvent('process', this, file) !== false){
35816             if(this.editable && file.type.indexOf('image') != -1){
35817                 this.fireEvent('edit', this, file);
35818                 return;
35819             }
35820
35821             this.uploadStart(file, false);
35822
35823             return;
35824         }
35825         
35826     },
35827     
35828     uploadStart : function(file, crop)
35829     {
35830         this.xhr = new XMLHttpRequest();
35831         
35832         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35833             this.arrange();
35834             return;
35835         }
35836         
35837         file.xhr = this.xhr;
35838             
35839         this.managerEl.createChild({
35840             tag : 'div',
35841             cls : 'roo-document-manager-loading',
35842             cn : [
35843                 {
35844                     tag : 'div',
35845                     tooltip : file.name,
35846                     cls : 'roo-document-manager-thumb',
35847                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35848                 }
35849             ]
35850
35851         });
35852
35853         this.xhr.open(this.method, this.url, true);
35854         
35855         var headers = {
35856             "Accept": "application/json",
35857             "Cache-Control": "no-cache",
35858             "X-Requested-With": "XMLHttpRequest"
35859         };
35860         
35861         for (var headerName in headers) {
35862             var headerValue = headers[headerName];
35863             if (headerValue) {
35864                 this.xhr.setRequestHeader(headerName, headerValue);
35865             }
35866         }
35867         
35868         var _this = this;
35869         
35870         this.xhr.onload = function()
35871         {
35872             _this.xhrOnLoad(_this.xhr);
35873         }
35874         
35875         this.xhr.onerror = function()
35876         {
35877             _this.xhrOnError(_this.xhr);
35878         }
35879         
35880         var formData = new FormData();
35881
35882         formData.append('returnHTML', 'NO');
35883         
35884         if(crop){
35885             formData.append('crop', crop);
35886         }
35887         
35888         formData.append(this.paramName, file, file.name);
35889         
35890         var options = {
35891             file : file, 
35892             manually : false
35893         };
35894         
35895         if(this.fireEvent('prepare', this, formData, options) != false){
35896             
35897             if(options.manually){
35898                 return;
35899             }
35900             
35901             this.xhr.send(formData);
35902             return;
35903         };
35904         
35905         this.uploadCancel();
35906     },
35907     
35908     uploadCancel : function()
35909     {
35910         if (this.xhr) {
35911             this.xhr.abort();
35912         }
35913         
35914         this.delegates = [];
35915         
35916         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35917             el.remove();
35918         }, this);
35919         
35920         this.arrange();
35921     },
35922     
35923     renderPreview : function(file)
35924     {
35925         if(typeof(file.target) != 'undefined' && file.target){
35926             return file;
35927         }
35928         
35929         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35930         
35931         var previewEl = this.managerEl.createChild({
35932             tag : 'div',
35933             cls : 'roo-document-manager-preview',
35934             cn : [
35935                 {
35936                     tag : 'div',
35937                     tooltip : file[this.toolTipName],
35938                     cls : 'roo-document-manager-thumb',
35939                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35940                 },
35941                 {
35942                     tag : 'button',
35943                     cls : 'close',
35944                     html : '<i class="fa fa-times-circle"></i>'
35945                 }
35946             ]
35947         });
35948
35949         var close = previewEl.select('button.close', true).first();
35950
35951         close.on('click', this.onRemove, this, file);
35952
35953         file.target = previewEl;
35954
35955         var image = previewEl.select('img', true).first();
35956         
35957         var _this = this;
35958         
35959         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35960         
35961         image.on('click', this.onClick, this, file);
35962         
35963         this.fireEvent('previewrendered', this, file);
35964         
35965         return file;
35966         
35967     },
35968     
35969     onPreviewLoad : function(file, image)
35970     {
35971         if(typeof(file.target) == 'undefined' || !file.target){
35972             return;
35973         }
35974         
35975         var width = image.dom.naturalWidth || image.dom.width;
35976         var height = image.dom.naturalHeight || image.dom.height;
35977         
35978         if(!this.previewResize) {
35979             return;
35980         }
35981         
35982         if(width > height){
35983             file.target.addClass('wide');
35984             return;
35985         }
35986         
35987         file.target.addClass('tall');
35988         return;
35989         
35990     },
35991     
35992     uploadFromSource : function(file, crop)
35993     {
35994         this.xhr = new XMLHttpRequest();
35995         
35996         this.managerEl.createChild({
35997             tag : 'div',
35998             cls : 'roo-document-manager-loading',
35999             cn : [
36000                 {
36001                     tag : 'div',
36002                     tooltip : file.name,
36003                     cls : 'roo-document-manager-thumb',
36004                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36005                 }
36006             ]
36007
36008         });
36009
36010         this.xhr.open(this.method, this.url, true);
36011         
36012         var headers = {
36013             "Accept": "application/json",
36014             "Cache-Control": "no-cache",
36015             "X-Requested-With": "XMLHttpRequest"
36016         };
36017         
36018         for (var headerName in headers) {
36019             var headerValue = headers[headerName];
36020             if (headerValue) {
36021                 this.xhr.setRequestHeader(headerName, headerValue);
36022             }
36023         }
36024         
36025         var _this = this;
36026         
36027         this.xhr.onload = function()
36028         {
36029             _this.xhrOnLoad(_this.xhr);
36030         }
36031         
36032         this.xhr.onerror = function()
36033         {
36034             _this.xhrOnError(_this.xhr);
36035         }
36036         
36037         var formData = new FormData();
36038
36039         formData.append('returnHTML', 'NO');
36040         
36041         formData.append('crop', crop);
36042         
36043         if(typeof(file.filename) != 'undefined'){
36044             formData.append('filename', file.filename);
36045         }
36046         
36047         if(typeof(file.mimetype) != 'undefined'){
36048             formData.append('mimetype', file.mimetype);
36049         }
36050         
36051         Roo.log(formData);
36052         
36053         if(this.fireEvent('prepare', this, formData) != false){
36054             this.xhr.send(formData);
36055         };
36056     }
36057 });
36058
36059 /*
36060 * Licence: LGPL
36061 */
36062
36063 /**
36064  * @class Roo.bootstrap.DocumentViewer
36065  * @extends Roo.bootstrap.Component
36066  * Bootstrap DocumentViewer class
36067  * @cfg {Boolean} showDownload (true|false) show download button (default true)
36068  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36069  * 
36070  * @constructor
36071  * Create a new DocumentViewer
36072  * @param {Object} config The config object
36073  */
36074
36075 Roo.bootstrap.DocumentViewer = function(config){
36076     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36077     
36078     this.addEvents({
36079         /**
36080          * @event initial
36081          * Fire after initEvent
36082          * @param {Roo.bootstrap.DocumentViewer} this
36083          */
36084         "initial" : true,
36085         /**
36086          * @event click
36087          * Fire after click
36088          * @param {Roo.bootstrap.DocumentViewer} this
36089          */
36090         "click" : true,
36091         /**
36092          * @event download
36093          * Fire after download button
36094          * @param {Roo.bootstrap.DocumentViewer} this
36095          */
36096         "download" : true,
36097         /**
36098          * @event trash
36099          * Fire after trash button
36100          * @param {Roo.bootstrap.DocumentViewer} this
36101          */
36102         "trash" : true
36103         
36104     });
36105 };
36106
36107 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
36108     
36109     showDownload : true,
36110     
36111     showTrash : true,
36112     
36113     getAutoCreate : function()
36114     {
36115         var cfg = {
36116             tag : 'div',
36117             cls : 'roo-document-viewer',
36118             cn : [
36119                 {
36120                     tag : 'div',
36121                     cls : 'roo-document-viewer-body',
36122                     cn : [
36123                         {
36124                             tag : 'div',
36125                             cls : 'roo-document-viewer-thumb',
36126                             cn : [
36127                                 {
36128                                     tag : 'img',
36129                                     cls : 'roo-document-viewer-image'
36130                                 }
36131                             ]
36132                         }
36133                     ]
36134                 },
36135                 {
36136                     tag : 'div',
36137                     cls : 'roo-document-viewer-footer',
36138                     cn : {
36139                         tag : 'div',
36140                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36141                         cn : [
36142                             {
36143                                 tag : 'div',
36144                                 cls : 'btn-group roo-document-viewer-download',
36145                                 cn : [
36146                                     {
36147                                         tag : 'button',
36148                                         cls : 'btn btn-default',
36149                                         html : '<i class="fa fa-download"></i>'
36150                                     }
36151                                 ]
36152                             },
36153                             {
36154                                 tag : 'div',
36155                                 cls : 'btn-group roo-document-viewer-trash',
36156                                 cn : [
36157                                     {
36158                                         tag : 'button',
36159                                         cls : 'btn btn-default',
36160                                         html : '<i class="fa fa-trash"></i>'
36161                                     }
36162                                 ]
36163                             }
36164                         ]
36165                     }
36166                 }
36167             ]
36168         };
36169         
36170         return cfg;
36171     },
36172     
36173     initEvents : function()
36174     {
36175         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36176         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36177         
36178         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36179         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36180         
36181         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36182         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36183         
36184         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36185         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36186         
36187         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36188         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36189         
36190         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36191         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36192         
36193         this.bodyEl.on('click', this.onClick, this);
36194         this.downloadBtn.on('click', this.onDownload, this);
36195         this.trashBtn.on('click', this.onTrash, this);
36196         
36197         this.downloadBtn.hide();
36198         this.trashBtn.hide();
36199         
36200         if(this.showDownload){
36201             this.downloadBtn.show();
36202         }
36203         
36204         if(this.showTrash){
36205             this.trashBtn.show();
36206         }
36207         
36208         if(!this.showDownload && !this.showTrash) {
36209             this.footerEl.hide();
36210         }
36211         
36212     },
36213     
36214     initial : function()
36215     {
36216         this.fireEvent('initial', this);
36217         
36218     },
36219     
36220     onClick : function(e)
36221     {
36222         e.preventDefault();
36223         
36224         this.fireEvent('click', this);
36225     },
36226     
36227     onDownload : function(e)
36228     {
36229         e.preventDefault();
36230         
36231         this.fireEvent('download', this);
36232     },
36233     
36234     onTrash : function(e)
36235     {
36236         e.preventDefault();
36237         
36238         this.fireEvent('trash', this);
36239     }
36240     
36241 });
36242 /*
36243  * - LGPL
36244  *
36245  * FieldLabel
36246  * 
36247  */
36248
36249 /**
36250  * @class Roo.bootstrap.form.FieldLabel
36251  * @extends Roo.bootstrap.Component
36252  * Bootstrap FieldLabel class
36253  * @cfg {String} html contents of the element
36254  * @cfg {String} tag tag of the element default label
36255  * @cfg {String} cls class of the element
36256  * @cfg {String} target label target 
36257  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36258  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36259  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36260  * @cfg {String} iconTooltip default "This field is required"
36261  * @cfg {String} indicatorpos (left|right) default left
36262  * 
36263  * @constructor
36264  * Create a new FieldLabel
36265  * @param {Object} config The config object
36266  */
36267
36268 Roo.bootstrap.form.FieldLabel = function(config){
36269     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36270     
36271     this.addEvents({
36272             /**
36273              * @event invalid
36274              * Fires after the field has been marked as invalid.
36275              * @param {Roo.form.FieldLabel} this
36276              * @param {String} msg The validation message
36277              */
36278             invalid : true,
36279             /**
36280              * @event valid
36281              * Fires after the field has been validated with no errors.
36282              * @param {Roo.form.FieldLabel} this
36283              */
36284             valid : true
36285         });
36286 };
36287
36288 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36289     
36290     tag: 'label',
36291     cls: '',
36292     html: '',
36293     target: '',
36294     allowBlank : true,
36295     invalidClass : 'has-warning',
36296     validClass : 'has-success',
36297     iconTooltip : 'This field is required',
36298     indicatorpos : 'left',
36299     
36300     getAutoCreate : function(){
36301         
36302         var cls = "";
36303         if (!this.allowBlank) {
36304             cls  = "visible";
36305         }
36306         
36307         var cfg = {
36308             tag : this.tag,
36309             cls : 'roo-bootstrap-field-label ' + this.cls,
36310             for : this.target,
36311             cn : [
36312                 {
36313                     tag : 'i',
36314                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36315                     tooltip : this.iconTooltip
36316                 },
36317                 {
36318                     tag : 'span',
36319                     html : this.html
36320                 }
36321             ] 
36322         };
36323         
36324         if(this.indicatorpos == 'right'){
36325             var cfg = {
36326                 tag : this.tag,
36327                 cls : 'roo-bootstrap-field-label ' + this.cls,
36328                 for : this.target,
36329                 cn : [
36330                     {
36331                         tag : 'span',
36332                         html : this.html
36333                     },
36334                     {
36335                         tag : 'i',
36336                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36337                         tooltip : this.iconTooltip
36338                     }
36339                 ] 
36340             };
36341         }
36342         
36343         return cfg;
36344     },
36345     
36346     initEvents: function() 
36347     {
36348         Roo.bootstrap.Element.superclass.initEvents.call(this);
36349         
36350         this.indicator = this.indicatorEl();
36351         
36352         if(this.indicator){
36353             this.indicator.removeClass('visible');
36354             this.indicator.addClass('invisible');
36355         }
36356         
36357         Roo.bootstrap.form.FieldLabel.register(this);
36358     },
36359     
36360     indicatorEl : function()
36361     {
36362         var indicator = this.el.select('i.roo-required-indicator',true).first();
36363         
36364         if(!indicator){
36365             return false;
36366         }
36367         
36368         return indicator;
36369         
36370     },
36371     
36372     /**
36373      * Mark this field as valid
36374      */
36375     markValid : function()
36376     {
36377         if(this.indicator){
36378             this.indicator.removeClass('visible');
36379             this.indicator.addClass('invisible');
36380         }
36381         if (Roo.bootstrap.version == 3) {
36382             this.el.removeClass(this.invalidClass);
36383             this.el.addClass(this.validClass);
36384         } else {
36385             this.el.removeClass('is-invalid');
36386             this.el.addClass('is-valid');
36387         }
36388         
36389         
36390         this.fireEvent('valid', this);
36391     },
36392     
36393     /**
36394      * Mark this field as invalid
36395      * @param {String} msg The validation message
36396      */
36397     markInvalid : function(msg)
36398     {
36399         if(this.indicator){
36400             this.indicator.removeClass('invisible');
36401             this.indicator.addClass('visible');
36402         }
36403           if (Roo.bootstrap.version == 3) {
36404             this.el.removeClass(this.validClass);
36405             this.el.addClass(this.invalidClass);
36406         } else {
36407             this.el.removeClass('is-valid');
36408             this.el.addClass('is-invalid');
36409         }
36410         
36411         
36412         this.fireEvent('invalid', this, msg);
36413     }
36414     
36415    
36416 });
36417
36418 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36419     
36420     groups: {},
36421     
36422      /**
36423     * register a FieldLabel Group
36424     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36425     */
36426     register : function(label)
36427     {
36428         if(this.groups.hasOwnProperty(label.target)){
36429             return;
36430         }
36431      
36432         this.groups[label.target] = label;
36433         
36434     },
36435     /**
36436     * fetch a FieldLabel Group based on the target
36437     * @param {string} target
36438     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36439     */
36440     get: function(target) {
36441         if (typeof(this.groups[target]) == 'undefined') {
36442             return false;
36443         }
36444         
36445         return this.groups[target] ;
36446     }
36447 });
36448
36449  
36450
36451  /*
36452  * - LGPL
36453  *
36454  * page DateSplitField.
36455  * 
36456  */
36457
36458
36459 /**
36460  * @class Roo.bootstrap.form.DateSplitField
36461  * @extends Roo.bootstrap.Component
36462  * Bootstrap DateSplitField class
36463  * @cfg {string} fieldLabel - the label associated
36464  * @cfg {Number} labelWidth set the width of label (0-12)
36465  * @cfg {String} labelAlign (top|left)
36466  * @cfg {Boolean} dayAllowBlank (true|false) default false
36467  * @cfg {Boolean} monthAllowBlank (true|false) default false
36468  * @cfg {Boolean} yearAllowBlank (true|false) default false
36469  * @cfg {string} dayPlaceholder 
36470  * @cfg {string} monthPlaceholder
36471  * @cfg {string} yearPlaceholder
36472  * @cfg {string} dayFormat default 'd'
36473  * @cfg {string} monthFormat default 'm'
36474  * @cfg {string} yearFormat default 'Y'
36475  * @cfg {Number} labellg set the width of label (1-12)
36476  * @cfg {Number} labelmd set the width of label (1-12)
36477  * @cfg {Number} labelsm set the width of label (1-12)
36478  * @cfg {Number} labelxs set the width of label (1-12)
36479
36480  *     
36481  * @constructor
36482  * Create a new DateSplitField
36483  * @param {Object} config The config object
36484  */
36485
36486 Roo.bootstrap.form.DateSplitField = function(config){
36487     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36488     
36489     this.addEvents({
36490         // raw events
36491          /**
36492          * @event years
36493          * getting the data of years
36494          * @param {Roo.bootstrap.form.DateSplitField} this
36495          * @param {Object} years
36496          */
36497         "years" : true,
36498         /**
36499          * @event days
36500          * getting the data of days
36501          * @param {Roo.bootstrap.form.DateSplitField} this
36502          * @param {Object} days
36503          */
36504         "days" : true,
36505         /**
36506          * @event invalid
36507          * Fires after the field has been marked as invalid.
36508          * @param {Roo.form.Field} this
36509          * @param {String} msg The validation message
36510          */
36511         invalid : true,
36512        /**
36513          * @event valid
36514          * Fires after the field has been validated with no errors.
36515          * @param {Roo.form.Field} this
36516          */
36517         valid : true
36518     });
36519 };
36520
36521 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36522     
36523     fieldLabel : '',
36524     labelAlign : 'top',
36525     labelWidth : 3,
36526     dayAllowBlank : false,
36527     monthAllowBlank : false,
36528     yearAllowBlank : false,
36529     dayPlaceholder : '',
36530     monthPlaceholder : '',
36531     yearPlaceholder : '',
36532     dayFormat : 'd',
36533     monthFormat : 'm',
36534     yearFormat : 'Y',
36535     isFormField : true,
36536     labellg : 0,
36537     labelmd : 0,
36538     labelsm : 0,
36539     labelxs : 0,
36540     
36541     getAutoCreate : function()
36542     {
36543         var cfg = {
36544             tag : 'div',
36545             cls : 'row roo-date-split-field-group',
36546             cn : [
36547                 {
36548                     tag : 'input',
36549                     type : 'hidden',
36550                     cls : 'form-hidden-field roo-date-split-field-group-value',
36551                     name : this.name
36552                 }
36553             ]
36554         };
36555         
36556         var labelCls = 'col-md-12';
36557         var contentCls = 'col-md-4';
36558         
36559         if(this.fieldLabel){
36560             
36561             var label = {
36562                 tag : 'div',
36563                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36564                 cn : [
36565                     {
36566                         tag : 'label',
36567                         html : this.fieldLabel
36568                     }
36569                 ]
36570             };
36571             
36572             if(this.labelAlign == 'left'){
36573             
36574                 if(this.labelWidth > 12){
36575                     label.style = "width: " + this.labelWidth + 'px';
36576                 }
36577
36578                 if(this.labelWidth < 13 && this.labelmd == 0){
36579                     this.labelmd = this.labelWidth;
36580                 }
36581
36582                 if(this.labellg > 0){
36583                     labelCls = ' col-lg-' + this.labellg;
36584                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36585                 }
36586
36587                 if(this.labelmd > 0){
36588                     labelCls = ' col-md-' + this.labelmd;
36589                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36590                 }
36591
36592                 if(this.labelsm > 0){
36593                     labelCls = ' col-sm-' + this.labelsm;
36594                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36595                 }
36596
36597                 if(this.labelxs > 0){
36598                     labelCls = ' col-xs-' + this.labelxs;
36599                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36600                 }
36601             }
36602             
36603             label.cls += ' ' + labelCls;
36604             
36605             cfg.cn.push(label);
36606         }
36607         
36608         Roo.each(['day', 'month', 'year'], function(t){
36609             cfg.cn.push({
36610                 tag : 'div',
36611                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36612             });
36613         }, this);
36614         
36615         return cfg;
36616     },
36617     
36618     inputEl: function ()
36619     {
36620         return this.el.select('.roo-date-split-field-group-value', true).first();
36621     },
36622     
36623     onRender : function(ct, position) 
36624     {
36625         var _this = this;
36626         
36627         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36628         
36629         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36630         
36631         this.dayField = new Roo.bootstrap.form.ComboBox({
36632             allowBlank : this.dayAllowBlank,
36633             alwaysQuery : true,
36634             displayField : 'value',
36635             editable : false,
36636             fieldLabel : '',
36637             forceSelection : true,
36638             mode : 'local',
36639             placeholder : this.dayPlaceholder,
36640             selectOnFocus : true,
36641             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36642             triggerAction : 'all',
36643             typeAhead : true,
36644             valueField : 'value',
36645             store : new Roo.data.SimpleStore({
36646                 data : (function() {    
36647                     var days = [];
36648                     _this.fireEvent('days', _this, days);
36649                     return days;
36650                 })(),
36651                 fields : [ 'value' ]
36652             }),
36653             listeners : {
36654                 select : function (_self, record, index)
36655                 {
36656                     _this.setValue(_this.getValue());
36657                 }
36658             }
36659         });
36660
36661         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36662         
36663         this.monthField = new Roo.bootstrap.form.MonthField({
36664             after : '<i class=\"fa fa-calendar\"></i>',
36665             allowBlank : this.monthAllowBlank,
36666             placeholder : this.monthPlaceholder,
36667             readOnly : true,
36668             listeners : {
36669                 render : function (_self)
36670                 {
36671                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36672                         e.preventDefault();
36673                         _self.focus();
36674                     });
36675                 },
36676                 select : function (_self, oldvalue, newvalue)
36677                 {
36678                     _this.setValue(_this.getValue());
36679                 }
36680             }
36681         });
36682         
36683         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36684         
36685         this.yearField = new Roo.bootstrap.form.ComboBox({
36686             allowBlank : this.yearAllowBlank,
36687             alwaysQuery : true,
36688             displayField : 'value',
36689             editable : false,
36690             fieldLabel : '',
36691             forceSelection : true,
36692             mode : 'local',
36693             placeholder : this.yearPlaceholder,
36694             selectOnFocus : true,
36695             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36696             triggerAction : 'all',
36697             typeAhead : true,
36698             valueField : 'value',
36699             store : new Roo.data.SimpleStore({
36700                 data : (function() {
36701                     var years = [];
36702                     _this.fireEvent('years', _this, years);
36703                     return years;
36704                 })(),
36705                 fields : [ 'value' ]
36706             }),
36707             listeners : {
36708                 select : function (_self, record, index)
36709                 {
36710                     _this.setValue(_this.getValue());
36711                 }
36712             }
36713         });
36714
36715         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36716     },
36717     
36718     setValue : function(v, format)
36719     {
36720         this.inputEl.dom.value = v;
36721         
36722         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36723         
36724         var d = Date.parseDate(v, f);
36725         
36726         if(!d){
36727             this.validate();
36728             return;
36729         }
36730         
36731         this.setDay(d.format(this.dayFormat));
36732         this.setMonth(d.format(this.monthFormat));
36733         this.setYear(d.format(this.yearFormat));
36734         
36735         this.validate();
36736         
36737         return;
36738     },
36739     
36740     setDay : function(v)
36741     {
36742         this.dayField.setValue(v);
36743         this.inputEl.dom.value = this.getValue();
36744         this.validate();
36745         return;
36746     },
36747     
36748     setMonth : function(v)
36749     {
36750         this.monthField.setValue(v, true);
36751         this.inputEl.dom.value = this.getValue();
36752         this.validate();
36753         return;
36754     },
36755     
36756     setYear : function(v)
36757     {
36758         this.yearField.setValue(v);
36759         this.inputEl.dom.value = this.getValue();
36760         this.validate();
36761         return;
36762     },
36763     
36764     getDay : function()
36765     {
36766         return this.dayField.getValue();
36767     },
36768     
36769     getMonth : function()
36770     {
36771         return this.monthField.getValue();
36772     },
36773     
36774     getYear : function()
36775     {
36776         return this.yearField.getValue();
36777     },
36778     
36779     getValue : function()
36780     {
36781         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36782         
36783         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36784         
36785         return date;
36786     },
36787     
36788     reset : function()
36789     {
36790         this.setDay('');
36791         this.setMonth('');
36792         this.setYear('');
36793         this.inputEl.dom.value = '';
36794         this.validate();
36795         return;
36796     },
36797     
36798     validate : function()
36799     {
36800         var d = this.dayField.validate();
36801         var m = this.monthField.validate();
36802         var y = this.yearField.validate();
36803         
36804         var valid = true;
36805         
36806         if(
36807                 (!this.dayAllowBlank && !d) ||
36808                 (!this.monthAllowBlank && !m) ||
36809                 (!this.yearAllowBlank && !y)
36810         ){
36811             valid = false;
36812         }
36813         
36814         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36815             return valid;
36816         }
36817         
36818         if(valid){
36819             this.markValid();
36820             return valid;
36821         }
36822         
36823         this.markInvalid();
36824         
36825         return valid;
36826     },
36827     
36828     markValid : function()
36829     {
36830         
36831         var label = this.el.select('label', true).first();
36832         var icon = this.el.select('i.fa-star', true).first();
36833
36834         if(label && icon){
36835             icon.remove();
36836         }
36837         
36838         this.fireEvent('valid', this);
36839     },
36840     
36841      /**
36842      * Mark this field as invalid
36843      * @param {String} msg The validation message
36844      */
36845     markInvalid : function(msg)
36846     {
36847         
36848         var label = this.el.select('label', true).first();
36849         var icon = this.el.select('i.fa-star', true).first();
36850
36851         if(label && !icon){
36852             this.el.select('.roo-date-split-field-label', true).createChild({
36853                 tag : 'i',
36854                 cls : 'text-danger fa fa-lg fa-star',
36855                 tooltip : 'This field is required',
36856                 style : 'margin-right:5px;'
36857             }, label, true);
36858         }
36859         
36860         this.fireEvent('invalid', this, msg);
36861     },
36862     
36863     clearInvalid : function()
36864     {
36865         var label = this.el.select('label', true).first();
36866         var icon = this.el.select('i.fa-star', true).first();
36867
36868         if(label && icon){
36869             icon.remove();
36870         }
36871         
36872         this.fireEvent('valid', this);
36873     },
36874     
36875     getName: function()
36876     {
36877         return this.name;
36878     }
36879     
36880 });
36881
36882  
36883
36884 /**
36885  * @class Roo.bootstrap.LayoutMasonry
36886  * @extends Roo.bootstrap.Component
36887  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36888  * Bootstrap Layout Masonry class
36889  *
36890  * This is based on 
36891  * http://masonry.desandro.com
36892  *
36893  * The idea is to render all the bricks based on vertical width...
36894  *
36895  * The original code extends 'outlayer' - we might need to use that....
36896
36897  * @constructor
36898  * Create a new Element
36899  * @param {Object} config The config object
36900  */
36901
36902 Roo.bootstrap.LayoutMasonry = function(config){
36903     
36904     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36905     
36906     this.bricks = [];
36907     
36908     Roo.bootstrap.LayoutMasonry.register(this);
36909     
36910     this.addEvents({
36911         // raw events
36912         /**
36913          * @event layout
36914          * Fire after layout the items
36915          * @param {Roo.bootstrap.LayoutMasonry} this
36916          * @param {Roo.EventObject} e
36917          */
36918         "layout" : true
36919     });
36920     
36921 };
36922
36923 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36924     
36925     /**
36926      * @cfg {Boolean} isLayoutInstant = no animation?
36927      */   
36928     isLayoutInstant : false, // needed?
36929    
36930     /**
36931      * @cfg {Number} boxWidth  width of the columns
36932      */   
36933     boxWidth : 450,
36934     
36935       /**
36936      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36937      */   
36938     boxHeight : 0,
36939     
36940     /**
36941      * @cfg {Number} padWidth padding below box..
36942      */   
36943     padWidth : 10, 
36944     
36945     /**
36946      * @cfg {Number} gutter gutter width..
36947      */   
36948     gutter : 10,
36949     
36950      /**
36951      * @cfg {Number} maxCols maximum number of columns
36952      */   
36953     
36954     maxCols: 0,
36955     
36956     /**
36957      * @cfg {Boolean} isAutoInitial defalut true
36958      */   
36959     isAutoInitial : true, 
36960     
36961     containerWidth: 0,
36962     
36963     /**
36964      * @cfg {Boolean} isHorizontal defalut false
36965      */   
36966     isHorizontal : false, 
36967
36968     currentSize : null,
36969     
36970     tag: 'div',
36971     
36972     cls: '',
36973     
36974     bricks: null, //CompositeElement
36975     
36976     cols : 1,
36977     
36978     _isLayoutInited : false,
36979     
36980 //    isAlternative : false, // only use for vertical layout...
36981     
36982     /**
36983      * @cfg {Number} alternativePadWidth padding below box..
36984      */   
36985     alternativePadWidth : 50,
36986     
36987     selectedBrick : [],
36988     
36989     getAutoCreate : function(){
36990         
36991         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36992         
36993         var cfg = {
36994             tag: this.tag,
36995             cls: 'blog-masonary-wrapper ' + this.cls,
36996             cn : {
36997                 cls : 'mas-boxes masonary'
36998             }
36999         };
37000         
37001         return cfg;
37002     },
37003     
37004     getChildContainer: function( )
37005     {
37006         if (this.boxesEl) {
37007             return this.boxesEl;
37008         }
37009         
37010         this.boxesEl = this.el.select('.mas-boxes').first();
37011         
37012         return this.boxesEl;
37013     },
37014     
37015     
37016     initEvents : function()
37017     {
37018         var _this = this;
37019         
37020         if(this.isAutoInitial){
37021             Roo.log('hook children rendered');
37022             this.on('childrenrendered', function() {
37023                 Roo.log('children rendered');
37024                 _this.initial();
37025             } ,this);
37026         }
37027     },
37028     
37029     initial : function()
37030     {
37031         this.selectedBrick = [];
37032         
37033         this.currentSize = this.el.getBox(true);
37034         
37035         Roo.EventManager.onWindowResize(this.resize, this); 
37036
37037         if(!this.isAutoInitial){
37038             this.layout();
37039             return;
37040         }
37041         
37042         this.layout();
37043         
37044         return;
37045         //this.layout.defer(500,this);
37046         
37047     },
37048     
37049     resize : function()
37050     {
37051         var cs = this.el.getBox(true);
37052         
37053         if (
37054                 this.currentSize.width == cs.width && 
37055                 this.currentSize.x == cs.x && 
37056                 this.currentSize.height == cs.height && 
37057                 this.currentSize.y == cs.y 
37058         ) {
37059             Roo.log("no change in with or X or Y");
37060             return;
37061         }
37062         
37063         this.currentSize = cs;
37064         
37065         this.layout();
37066         
37067     },
37068     
37069     layout : function()
37070     {   
37071         this._resetLayout();
37072         
37073         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37074         
37075         this.layoutItems( isInstant );
37076       
37077         this._isLayoutInited = true;
37078         
37079         this.fireEvent('layout', this);
37080         
37081     },
37082     
37083     _resetLayout : function()
37084     {
37085         if(this.isHorizontal){
37086             this.horizontalMeasureColumns();
37087             return;
37088         }
37089         
37090         this.verticalMeasureColumns();
37091         
37092     },
37093     
37094     verticalMeasureColumns : function()
37095     {
37096         this.getContainerWidth();
37097         
37098 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37099 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
37100 //            return;
37101 //        }
37102         
37103         var boxWidth = this.boxWidth + this.padWidth;
37104         
37105         if(this.containerWidth < this.boxWidth){
37106             boxWidth = this.containerWidth
37107         }
37108         
37109         var containerWidth = this.containerWidth;
37110         
37111         var cols = Math.floor(containerWidth / boxWidth);
37112         
37113         this.cols = Math.max( cols, 1 );
37114         
37115         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37116         
37117         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37118         
37119         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37120         
37121         this.colWidth = boxWidth + avail - this.padWidth;
37122         
37123         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37124         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
37125     },
37126     
37127     horizontalMeasureColumns : function()
37128     {
37129         this.getContainerWidth();
37130         
37131         var boxWidth = this.boxWidth;
37132         
37133         if(this.containerWidth < boxWidth){
37134             boxWidth = this.containerWidth;
37135         }
37136         
37137         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37138         
37139         this.el.setHeight(boxWidth);
37140         
37141     },
37142     
37143     getContainerWidth : function()
37144     {
37145         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
37146     },
37147     
37148     layoutItems : function( isInstant )
37149     {
37150         Roo.log(this.bricks);
37151         
37152         var items = Roo.apply([], this.bricks);
37153         
37154         if(this.isHorizontal){
37155             this._horizontalLayoutItems( items , isInstant );
37156             return;
37157         }
37158         
37159 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37160 //            this._verticalAlternativeLayoutItems( items , isInstant );
37161 //            return;
37162 //        }
37163         
37164         this._verticalLayoutItems( items , isInstant );
37165         
37166     },
37167     
37168     _verticalLayoutItems : function ( items , isInstant)
37169     {
37170         if ( !items || !items.length ) {
37171             return;
37172         }
37173         
37174         var standard = [
37175             ['xs', 'xs', 'xs', 'tall'],
37176             ['xs', 'xs', 'tall'],
37177             ['xs', 'xs', 'sm'],
37178             ['xs', 'xs', 'xs'],
37179             ['xs', 'tall'],
37180             ['xs', 'sm'],
37181             ['xs', 'xs'],
37182             ['xs'],
37183             
37184             ['sm', 'xs', 'xs'],
37185             ['sm', 'xs'],
37186             ['sm'],
37187             
37188             ['tall', 'xs', 'xs', 'xs'],
37189             ['tall', 'xs', 'xs'],
37190             ['tall', 'xs'],
37191             ['tall']
37192             
37193         ];
37194         
37195         var queue = [];
37196         
37197         var boxes = [];
37198         
37199         var box = [];
37200         
37201         Roo.each(items, function(item, k){
37202             
37203             switch (item.size) {
37204                 // these layouts take up a full box,
37205                 case 'md' :
37206                 case 'md-left' :
37207                 case 'md-right' :
37208                 case 'wide' :
37209                     
37210                     if(box.length){
37211                         boxes.push(box);
37212                         box = [];
37213                     }
37214                     
37215                     boxes.push([item]);
37216                     
37217                     break;
37218                     
37219                 case 'xs' :
37220                 case 'sm' :
37221                 case 'tall' :
37222                     
37223                     box.push(item);
37224                     
37225                     break;
37226                 default :
37227                     break;
37228                     
37229             }
37230             
37231         }, this);
37232         
37233         if(box.length){
37234             boxes.push(box);
37235             box = [];
37236         }
37237         
37238         var filterPattern = function(box, length)
37239         {
37240             if(!box.length){
37241                 return;
37242             }
37243             
37244             var match = false;
37245             
37246             var pattern = box.slice(0, length);
37247             
37248             var format = [];
37249             
37250             Roo.each(pattern, function(i){
37251                 format.push(i.size);
37252             }, this);
37253             
37254             Roo.each(standard, function(s){
37255                 
37256                 if(String(s) != String(format)){
37257                     return;
37258                 }
37259                 
37260                 match = true;
37261                 return false;
37262                 
37263             }, this);
37264             
37265             if(!match && length == 1){
37266                 return;
37267             }
37268             
37269             if(!match){
37270                 filterPattern(box, length - 1);
37271                 return;
37272             }
37273                 
37274             queue.push(pattern);
37275
37276             box = box.slice(length, box.length);
37277
37278             filterPattern(box, 4);
37279
37280             return;
37281             
37282         }
37283         
37284         Roo.each(boxes, function(box, k){
37285             
37286             if(!box.length){
37287                 return;
37288             }
37289             
37290             if(box.length == 1){
37291                 queue.push(box);
37292                 return;
37293             }
37294             
37295             filterPattern(box, 4);
37296             
37297         }, this);
37298         
37299         this._processVerticalLayoutQueue( queue, isInstant );
37300         
37301     },
37302     
37303 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37304 //    {
37305 //        if ( !items || !items.length ) {
37306 //            return;
37307 //        }
37308 //
37309 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37310 //        
37311 //    },
37312     
37313     _horizontalLayoutItems : function ( items , isInstant)
37314     {
37315         if ( !items || !items.length || items.length < 3) {
37316             return;
37317         }
37318         
37319         items.reverse();
37320         
37321         var eItems = items.slice(0, 3);
37322         
37323         items = items.slice(3, items.length);
37324         
37325         var standard = [
37326             ['xs', 'xs', 'xs', 'wide'],
37327             ['xs', 'xs', 'wide'],
37328             ['xs', 'xs', 'sm'],
37329             ['xs', 'xs', 'xs'],
37330             ['xs', 'wide'],
37331             ['xs', 'sm'],
37332             ['xs', 'xs'],
37333             ['xs'],
37334             
37335             ['sm', 'xs', 'xs'],
37336             ['sm', 'xs'],
37337             ['sm'],
37338             
37339             ['wide', 'xs', 'xs', 'xs'],
37340             ['wide', 'xs', 'xs'],
37341             ['wide', 'xs'],
37342             ['wide'],
37343             
37344             ['wide-thin']
37345         ];
37346         
37347         var queue = [];
37348         
37349         var boxes = [];
37350         
37351         var box = [];
37352         
37353         Roo.each(items, function(item, k){
37354             
37355             switch (item.size) {
37356                 case 'md' :
37357                 case 'md-left' :
37358                 case 'md-right' :
37359                 case 'tall' :
37360                     
37361                     if(box.length){
37362                         boxes.push(box);
37363                         box = [];
37364                     }
37365                     
37366                     boxes.push([item]);
37367                     
37368                     break;
37369                     
37370                 case 'xs' :
37371                 case 'sm' :
37372                 case 'wide' :
37373                 case 'wide-thin' :
37374                     
37375                     box.push(item);
37376                     
37377                     break;
37378                 default :
37379                     break;
37380                     
37381             }
37382             
37383         }, this);
37384         
37385         if(box.length){
37386             boxes.push(box);
37387             box = [];
37388         }
37389         
37390         var filterPattern = function(box, length)
37391         {
37392             if(!box.length){
37393                 return;
37394             }
37395             
37396             var match = false;
37397             
37398             var pattern = box.slice(0, length);
37399             
37400             var format = [];
37401             
37402             Roo.each(pattern, function(i){
37403                 format.push(i.size);
37404             }, this);
37405             
37406             Roo.each(standard, function(s){
37407                 
37408                 if(String(s) != String(format)){
37409                     return;
37410                 }
37411                 
37412                 match = true;
37413                 return false;
37414                 
37415             }, this);
37416             
37417             if(!match && length == 1){
37418                 return;
37419             }
37420             
37421             if(!match){
37422                 filterPattern(box, length - 1);
37423                 return;
37424             }
37425                 
37426             queue.push(pattern);
37427
37428             box = box.slice(length, box.length);
37429
37430             filterPattern(box, 4);
37431
37432             return;
37433             
37434         }
37435         
37436         Roo.each(boxes, function(box, k){
37437             
37438             if(!box.length){
37439                 return;
37440             }
37441             
37442             if(box.length == 1){
37443                 queue.push(box);
37444                 return;
37445             }
37446             
37447             filterPattern(box, 4);
37448             
37449         }, this);
37450         
37451         
37452         var prune = [];
37453         
37454         var pos = this.el.getBox(true);
37455         
37456         var minX = pos.x;
37457         
37458         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37459         
37460         var hit_end = false;
37461         
37462         Roo.each(queue, function(box){
37463             
37464             if(hit_end){
37465                 
37466                 Roo.each(box, function(b){
37467                 
37468                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37469                     b.el.hide();
37470
37471                 }, this);
37472
37473                 return;
37474             }
37475             
37476             var mx = 0;
37477             
37478             Roo.each(box, function(b){
37479                 
37480                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37481                 b.el.show();
37482
37483                 mx = Math.max(mx, b.x);
37484                 
37485             }, this);
37486             
37487             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37488             
37489             if(maxX < minX){
37490                 
37491                 Roo.each(box, function(b){
37492                 
37493                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37494                     b.el.hide();
37495                     
37496                 }, this);
37497                 
37498                 hit_end = true;
37499                 
37500                 return;
37501             }
37502             
37503             prune.push(box);
37504             
37505         }, this);
37506         
37507         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37508     },
37509     
37510     /** Sets position of item in DOM
37511     * @param {Element} item
37512     * @param {Number} x - horizontal position
37513     * @param {Number} y - vertical position
37514     * @param {Boolean} isInstant - disables transitions
37515     */
37516     _processVerticalLayoutQueue : function( queue, isInstant )
37517     {
37518         var pos = this.el.getBox(true);
37519         var x = pos.x;
37520         var y = pos.y;
37521         var maxY = [];
37522         
37523         for (var i = 0; i < this.cols; i++){
37524             maxY[i] = pos.y;
37525         }
37526         
37527         Roo.each(queue, function(box, k){
37528             
37529             var col = k % this.cols;
37530             
37531             Roo.each(box, function(b,kk){
37532                 
37533                 b.el.position('absolute');
37534                 
37535                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37536                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37537                 
37538                 if(b.size == 'md-left' || b.size == 'md-right'){
37539                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37540                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37541                 }
37542                 
37543                 b.el.setWidth(width);
37544                 b.el.setHeight(height);
37545                 // iframe?
37546                 b.el.select('iframe',true).setSize(width,height);
37547                 
37548             }, this);
37549             
37550             for (var i = 0; i < this.cols; i++){
37551                 
37552                 if(maxY[i] < maxY[col]){
37553                     col = i;
37554                     continue;
37555                 }
37556                 
37557                 col = Math.min(col, i);
37558                 
37559             }
37560             
37561             x = pos.x + col * (this.colWidth + this.padWidth);
37562             
37563             y = maxY[col];
37564             
37565             var positions = [];
37566             
37567             switch (box.length){
37568                 case 1 :
37569                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37570                     break;
37571                 case 2 :
37572                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37573                     break;
37574                 case 3 :
37575                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37576                     break;
37577                 case 4 :
37578                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37579                     break;
37580                 default :
37581                     break;
37582             }
37583             
37584             Roo.each(box, function(b,kk){
37585                 
37586                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37587                 
37588                 var sz = b.el.getSize();
37589                 
37590                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37591                 
37592             }, this);
37593             
37594         }, this);
37595         
37596         var mY = 0;
37597         
37598         for (var i = 0; i < this.cols; i++){
37599             mY = Math.max(mY, maxY[i]);
37600         }
37601         
37602         this.el.setHeight(mY - pos.y);
37603         
37604     },
37605     
37606 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37607 //    {
37608 //        var pos = this.el.getBox(true);
37609 //        var x = pos.x;
37610 //        var y = pos.y;
37611 //        var maxX = pos.right;
37612 //        
37613 //        var maxHeight = 0;
37614 //        
37615 //        Roo.each(items, function(item, k){
37616 //            
37617 //            var c = k % 2;
37618 //            
37619 //            item.el.position('absolute');
37620 //                
37621 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37622 //
37623 //            item.el.setWidth(width);
37624 //
37625 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37626 //
37627 //            item.el.setHeight(height);
37628 //            
37629 //            if(c == 0){
37630 //                item.el.setXY([x, y], isInstant ? false : true);
37631 //            } else {
37632 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37633 //            }
37634 //            
37635 //            y = y + height + this.alternativePadWidth;
37636 //            
37637 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37638 //            
37639 //        }, this);
37640 //        
37641 //        this.el.setHeight(maxHeight);
37642 //        
37643 //    },
37644     
37645     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37646     {
37647         var pos = this.el.getBox(true);
37648         
37649         var minX = pos.x;
37650         var minY = pos.y;
37651         
37652         var maxX = pos.right;
37653         
37654         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37655         
37656         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37657         
37658         Roo.each(queue, function(box, k){
37659             
37660             Roo.each(box, function(b, kk){
37661                 
37662                 b.el.position('absolute');
37663                 
37664                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37665                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37666                 
37667                 if(b.size == 'md-left' || b.size == 'md-right'){
37668                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37669                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37670                 }
37671                 
37672                 b.el.setWidth(width);
37673                 b.el.setHeight(height);
37674                 
37675             }, this);
37676             
37677             if(!box.length){
37678                 return;
37679             }
37680             
37681             var positions = [];
37682             
37683             switch (box.length){
37684                 case 1 :
37685                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37686                     break;
37687                 case 2 :
37688                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37689                     break;
37690                 case 3 :
37691                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37692                     break;
37693                 case 4 :
37694                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37695                     break;
37696                 default :
37697                     break;
37698             }
37699             
37700             Roo.each(box, function(b,kk){
37701                 
37702                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37703                 
37704                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37705                 
37706             }, this);
37707             
37708         }, this);
37709         
37710     },
37711     
37712     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37713     {
37714         Roo.each(eItems, function(b,k){
37715             
37716             b.size = (k == 0) ? 'sm' : 'xs';
37717             b.x = (k == 0) ? 2 : 1;
37718             b.y = (k == 0) ? 2 : 1;
37719             
37720             b.el.position('absolute');
37721             
37722             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37723                 
37724             b.el.setWidth(width);
37725             
37726             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37727             
37728             b.el.setHeight(height);
37729             
37730         }, this);
37731
37732         var positions = [];
37733         
37734         positions.push({
37735             x : maxX - this.unitWidth * 2 - this.gutter,
37736             y : minY
37737         });
37738         
37739         positions.push({
37740             x : maxX - this.unitWidth,
37741             y : minY + (this.unitWidth + this.gutter) * 2
37742         });
37743         
37744         positions.push({
37745             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37746             y : minY
37747         });
37748         
37749         Roo.each(eItems, function(b,k){
37750             
37751             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37752
37753         }, this);
37754         
37755     },
37756     
37757     getVerticalOneBoxColPositions : function(x, y, box)
37758     {
37759         var pos = [];
37760         
37761         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37762         
37763         if(box[0].size == 'md-left'){
37764             rand = 0;
37765         }
37766         
37767         if(box[0].size == 'md-right'){
37768             rand = 1;
37769         }
37770         
37771         pos.push({
37772             x : x + (this.unitWidth + this.gutter) * rand,
37773             y : y
37774         });
37775         
37776         return pos;
37777     },
37778     
37779     getVerticalTwoBoxColPositions : function(x, y, box)
37780     {
37781         var pos = [];
37782         
37783         if(box[0].size == 'xs'){
37784             
37785             pos.push({
37786                 x : x,
37787                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37788             });
37789
37790             pos.push({
37791                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37792                 y : y
37793             });
37794             
37795             return pos;
37796             
37797         }
37798         
37799         pos.push({
37800             x : x,
37801             y : y
37802         });
37803
37804         pos.push({
37805             x : x + (this.unitWidth + this.gutter) * 2,
37806             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37807         });
37808         
37809         return pos;
37810         
37811     },
37812     
37813     getVerticalThreeBoxColPositions : function(x, y, box)
37814     {
37815         var pos = [];
37816         
37817         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37818             
37819             pos.push({
37820                 x : x,
37821                 y : y
37822             });
37823
37824             pos.push({
37825                 x : x + (this.unitWidth + this.gutter) * 1,
37826                 y : y
37827             });
37828             
37829             pos.push({
37830                 x : x + (this.unitWidth + this.gutter) * 2,
37831                 y : y
37832             });
37833             
37834             return pos;
37835             
37836         }
37837         
37838         if(box[0].size == 'xs' && box[1].size == 'xs'){
37839             
37840             pos.push({
37841                 x : x,
37842                 y : y
37843             });
37844
37845             pos.push({
37846                 x : x,
37847                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37848             });
37849             
37850             pos.push({
37851                 x : x + (this.unitWidth + this.gutter) * 1,
37852                 y : y
37853             });
37854             
37855             return pos;
37856             
37857         }
37858         
37859         pos.push({
37860             x : x,
37861             y : y
37862         });
37863
37864         pos.push({
37865             x : x + (this.unitWidth + this.gutter) * 2,
37866             y : y
37867         });
37868
37869         pos.push({
37870             x : x + (this.unitWidth + this.gutter) * 2,
37871             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37872         });
37873             
37874         return pos;
37875         
37876     },
37877     
37878     getVerticalFourBoxColPositions : function(x, y, box)
37879     {
37880         var pos = [];
37881         
37882         if(box[0].size == 'xs'){
37883             
37884             pos.push({
37885                 x : x,
37886                 y : y
37887             });
37888
37889             pos.push({
37890                 x : x,
37891                 y : y + (this.unitHeight + this.gutter) * 1
37892             });
37893             
37894             pos.push({
37895                 x : x,
37896                 y : y + (this.unitHeight + this.gutter) * 2
37897             });
37898             
37899             pos.push({
37900                 x : x + (this.unitWidth + this.gutter) * 1,
37901                 y : y
37902             });
37903             
37904             return pos;
37905             
37906         }
37907         
37908         pos.push({
37909             x : x,
37910             y : y
37911         });
37912
37913         pos.push({
37914             x : x + (this.unitWidth + this.gutter) * 2,
37915             y : y
37916         });
37917
37918         pos.push({
37919             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37920             y : y + (this.unitHeight + this.gutter) * 1
37921         });
37922
37923         pos.push({
37924             x : x + (this.unitWidth + this.gutter) * 2,
37925             y : y + (this.unitWidth + this.gutter) * 2
37926         });
37927
37928         return pos;
37929         
37930     },
37931     
37932     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37933     {
37934         var pos = [];
37935         
37936         if(box[0].size == 'md-left'){
37937             pos.push({
37938                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37939                 y : minY
37940             });
37941             
37942             return pos;
37943         }
37944         
37945         if(box[0].size == 'md-right'){
37946             pos.push({
37947                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37948                 y : minY + (this.unitWidth + this.gutter) * 1
37949             });
37950             
37951             return pos;
37952         }
37953         
37954         var rand = Math.floor(Math.random() * (4 - box[0].y));
37955         
37956         pos.push({
37957             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37958             y : minY + (this.unitWidth + this.gutter) * rand
37959         });
37960         
37961         return pos;
37962         
37963     },
37964     
37965     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37966     {
37967         var pos = [];
37968         
37969         if(box[0].size == 'xs'){
37970             
37971             pos.push({
37972                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37973                 y : minY
37974             });
37975
37976             pos.push({
37977                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37978                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37979             });
37980             
37981             return pos;
37982             
37983         }
37984         
37985         pos.push({
37986             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37987             y : minY
37988         });
37989
37990         pos.push({
37991             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37992             y : minY + (this.unitWidth + this.gutter) * 2
37993         });
37994         
37995         return pos;
37996         
37997     },
37998     
37999     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
38000     {
38001         var pos = [];
38002         
38003         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38004             
38005             pos.push({
38006                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38007                 y : minY
38008             });
38009
38010             pos.push({
38011                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38012                 y : minY + (this.unitWidth + this.gutter) * 1
38013             });
38014             
38015             pos.push({
38016                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38017                 y : minY + (this.unitWidth + this.gutter) * 2
38018             });
38019             
38020             return pos;
38021             
38022         }
38023         
38024         if(box[0].size == 'xs' && box[1].size == 'xs'){
38025             
38026             pos.push({
38027                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38028                 y : minY
38029             });
38030
38031             pos.push({
38032                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38033                 y : minY
38034             });
38035             
38036             pos.push({
38037                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38038                 y : minY + (this.unitWidth + this.gutter) * 1
38039             });
38040             
38041             return pos;
38042             
38043         }
38044         
38045         pos.push({
38046             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38047             y : minY
38048         });
38049
38050         pos.push({
38051             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38052             y : minY + (this.unitWidth + this.gutter) * 2
38053         });
38054
38055         pos.push({
38056             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38057             y : minY + (this.unitWidth + this.gutter) * 2
38058         });
38059             
38060         return pos;
38061         
38062     },
38063     
38064     getHorizontalFourBoxColPositions : function(maxX, minY, box)
38065     {
38066         var pos = [];
38067         
38068         if(box[0].size == 'xs'){
38069             
38070             pos.push({
38071                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38072                 y : minY
38073             });
38074
38075             pos.push({
38076                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38077                 y : minY
38078             });
38079             
38080             pos.push({
38081                 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),
38082                 y : minY
38083             });
38084             
38085             pos.push({
38086                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38087                 y : minY + (this.unitWidth + this.gutter) * 1
38088             });
38089             
38090             return pos;
38091             
38092         }
38093         
38094         pos.push({
38095             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38096             y : minY
38097         });
38098         
38099         pos.push({
38100             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38101             y : minY + (this.unitWidth + this.gutter) * 2
38102         });
38103         
38104         pos.push({
38105             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38106             y : minY + (this.unitWidth + this.gutter) * 2
38107         });
38108         
38109         pos.push({
38110             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),
38111             y : minY + (this.unitWidth + this.gutter) * 2
38112         });
38113
38114         return pos;
38115         
38116     },
38117     
38118     /**
38119     * remove a Masonry Brick
38120     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38121     */
38122     removeBrick : function(brick_id)
38123     {
38124         if (!brick_id) {
38125             return;
38126         }
38127         
38128         for (var i = 0; i<this.bricks.length; i++) {
38129             if (this.bricks[i].id == brick_id) {
38130                 this.bricks.splice(i,1);
38131                 this.el.dom.removeChild(Roo.get(brick_id).dom);
38132                 this.initial();
38133             }
38134         }
38135     },
38136     
38137     /**
38138     * adds a Masonry Brick
38139     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38140     */
38141     addBrick : function(cfg)
38142     {
38143         var cn = new Roo.bootstrap.MasonryBrick(cfg);
38144         //this.register(cn);
38145         cn.parentId = this.id;
38146         cn.render(this.el);
38147         return cn;
38148     },
38149     
38150     /**
38151     * register a Masonry Brick
38152     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38153     */
38154     
38155     register : function(brick)
38156     {
38157         this.bricks.push(brick);
38158         brick.masonryId = this.id;
38159     },
38160     
38161     /**
38162     * clear all the Masonry Brick
38163     */
38164     clearAll : function()
38165     {
38166         this.bricks = [];
38167         //this.getChildContainer().dom.innerHTML = "";
38168         this.el.dom.innerHTML = '';
38169     },
38170     
38171     getSelected : function()
38172     {
38173         if (!this.selectedBrick) {
38174             return false;
38175         }
38176         
38177         return this.selectedBrick;
38178     }
38179 });
38180
38181 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38182     
38183     groups: {},
38184      /**
38185     * register a Masonry Layout
38186     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38187     */
38188     
38189     register : function(layout)
38190     {
38191         this.groups[layout.id] = layout;
38192     },
38193     /**
38194     * fetch a  Masonry Layout based on the masonry layout ID
38195     * @param {string} the masonry layout to add
38196     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38197     */
38198     
38199     get: function(layout_id) {
38200         if (typeof(this.groups[layout_id]) == 'undefined') {
38201             return false;
38202         }
38203         return this.groups[layout_id] ;
38204     }
38205     
38206     
38207     
38208 });
38209
38210  
38211
38212  /**
38213  *
38214  * This is based on 
38215  * http://masonry.desandro.com
38216  *
38217  * The idea is to render all the bricks based on vertical width...
38218  *
38219  * The original code extends 'outlayer' - we might need to use that....
38220  * 
38221  */
38222
38223
38224 /**
38225  * @class Roo.bootstrap.LayoutMasonryAuto
38226  * @extends Roo.bootstrap.Component
38227  * Bootstrap Layout Masonry class
38228  * 
38229  * @constructor
38230  * Create a new Element
38231  * @param {Object} config The config object
38232  */
38233
38234 Roo.bootstrap.LayoutMasonryAuto = function(config){
38235     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38236 };
38237
38238 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38239     
38240       /**
38241      * @cfg {Boolean} isFitWidth  - resize the width..
38242      */   
38243     isFitWidth : false,  // options..
38244     /**
38245      * @cfg {Boolean} isOriginLeft = left align?
38246      */   
38247     isOriginLeft : true,
38248     /**
38249      * @cfg {Boolean} isOriginTop = top align?
38250      */   
38251     isOriginTop : false,
38252     /**
38253      * @cfg {Boolean} isLayoutInstant = no animation?
38254      */   
38255     isLayoutInstant : false, // needed?
38256     /**
38257      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38258      */   
38259     isResizingContainer : true,
38260     /**
38261      * @cfg {Number} columnWidth  width of the columns 
38262      */   
38263     
38264     columnWidth : 0,
38265     
38266     /**
38267      * @cfg {Number} maxCols maximum number of columns
38268      */   
38269     
38270     maxCols: 0,
38271     /**
38272      * @cfg {Number} padHeight padding below box..
38273      */   
38274     
38275     padHeight : 10, 
38276     
38277     /**
38278      * @cfg {Boolean} isAutoInitial defalut true
38279      */   
38280     
38281     isAutoInitial : true, 
38282     
38283     // private?
38284     gutter : 0,
38285     
38286     containerWidth: 0,
38287     initialColumnWidth : 0,
38288     currentSize : null,
38289     
38290     colYs : null, // array.
38291     maxY : 0,
38292     padWidth: 10,
38293     
38294     
38295     tag: 'div',
38296     cls: '',
38297     bricks: null, //CompositeElement
38298     cols : 0, // array?
38299     // element : null, // wrapped now this.el
38300     _isLayoutInited : null, 
38301     
38302     
38303     getAutoCreate : function(){
38304         
38305         var cfg = {
38306             tag: this.tag,
38307             cls: 'blog-masonary-wrapper ' + this.cls,
38308             cn : {
38309                 cls : 'mas-boxes masonary'
38310             }
38311         };
38312         
38313         return cfg;
38314     },
38315     
38316     getChildContainer: function( )
38317     {
38318         if (this.boxesEl) {
38319             return this.boxesEl;
38320         }
38321         
38322         this.boxesEl = this.el.select('.mas-boxes').first();
38323         
38324         return this.boxesEl;
38325     },
38326     
38327     
38328     initEvents : function()
38329     {
38330         var _this = this;
38331         
38332         if(this.isAutoInitial){
38333             Roo.log('hook children rendered');
38334             this.on('childrenrendered', function() {
38335                 Roo.log('children rendered');
38336                 _this.initial();
38337             } ,this);
38338         }
38339         
38340     },
38341     
38342     initial : function()
38343     {
38344         this.reloadItems();
38345
38346         this.currentSize = this.el.getBox(true);
38347
38348         /// was window resize... - let's see if this works..
38349         Roo.EventManager.onWindowResize(this.resize, this); 
38350
38351         if(!this.isAutoInitial){
38352             this.layout();
38353             return;
38354         }
38355         
38356         this.layout.defer(500,this);
38357     },
38358     
38359     reloadItems: function()
38360     {
38361         this.bricks = this.el.select('.masonry-brick', true);
38362         
38363         this.bricks.each(function(b) {
38364             //Roo.log(b.getSize());
38365             if (!b.attr('originalwidth')) {
38366                 b.attr('originalwidth',  b.getSize().width);
38367             }
38368             
38369         });
38370         
38371         Roo.log(this.bricks.elements.length);
38372     },
38373     
38374     resize : function()
38375     {
38376         Roo.log('resize');
38377         var cs = this.el.getBox(true);
38378         
38379         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38380             Roo.log("no change in with or X");
38381             return;
38382         }
38383         this.currentSize = cs;
38384         this.layout();
38385     },
38386     
38387     layout : function()
38388     {
38389          Roo.log('layout');
38390         this._resetLayout();
38391         //this._manageStamps();
38392       
38393         // don't animate first layout
38394         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38395         this.layoutItems( isInstant );
38396       
38397         // flag for initalized
38398         this._isLayoutInited = true;
38399     },
38400     
38401     layoutItems : function( isInstant )
38402     {
38403         //var items = this._getItemsForLayout( this.items );
38404         // original code supports filtering layout items.. we just ignore it..
38405         
38406         this._layoutItems( this.bricks , isInstant );
38407       
38408         this._postLayout();
38409     },
38410     _layoutItems : function ( items , isInstant)
38411     {
38412        //this.fireEvent( 'layout', this, items );
38413     
38414
38415         if ( !items || !items.elements.length ) {
38416           // no items, emit event with empty array
38417             return;
38418         }
38419
38420         var queue = [];
38421         items.each(function(item) {
38422             Roo.log("layout item");
38423             Roo.log(item);
38424             // get x/y object from method
38425             var position = this._getItemLayoutPosition( item );
38426             // enqueue
38427             position.item = item;
38428             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38429             queue.push( position );
38430         }, this);
38431       
38432         this._processLayoutQueue( queue );
38433     },
38434     /** Sets position of item in DOM
38435     * @param {Element} item
38436     * @param {Number} x - horizontal position
38437     * @param {Number} y - vertical position
38438     * @param {Boolean} isInstant - disables transitions
38439     */
38440     _processLayoutQueue : function( queue )
38441     {
38442         for ( var i=0, len = queue.length; i < len; i++ ) {
38443             var obj = queue[i];
38444             obj.item.position('absolute');
38445             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38446         }
38447     },
38448       
38449     
38450     /**
38451     * Any logic you want to do after each layout,
38452     * i.e. size the container
38453     */
38454     _postLayout : function()
38455     {
38456         this.resizeContainer();
38457     },
38458     
38459     resizeContainer : function()
38460     {
38461         if ( !this.isResizingContainer ) {
38462             return;
38463         }
38464         var size = this._getContainerSize();
38465         if ( size ) {
38466             this.el.setSize(size.width,size.height);
38467             this.boxesEl.setSize(size.width,size.height);
38468         }
38469     },
38470     
38471     
38472     
38473     _resetLayout : function()
38474     {
38475         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38476         this.colWidth = this.el.getWidth();
38477         //this.gutter = this.el.getWidth(); 
38478         
38479         this.measureColumns();
38480
38481         // reset column Y
38482         var i = this.cols;
38483         this.colYs = [];
38484         while (i--) {
38485             this.colYs.push( 0 );
38486         }
38487     
38488         this.maxY = 0;
38489     },
38490
38491     measureColumns : function()
38492     {
38493         this.getContainerWidth();
38494       // if columnWidth is 0, default to outerWidth of first item
38495         if ( !this.columnWidth ) {
38496             var firstItem = this.bricks.first();
38497             Roo.log(firstItem);
38498             this.columnWidth  = this.containerWidth;
38499             if (firstItem && firstItem.attr('originalwidth') ) {
38500                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38501             }
38502             // columnWidth fall back to item of first element
38503             Roo.log("set column width?");
38504                         this.initialColumnWidth = this.columnWidth  ;
38505
38506             // if first elem has no width, default to size of container
38507             
38508         }
38509         
38510         
38511         if (this.initialColumnWidth) {
38512             this.columnWidth = this.initialColumnWidth;
38513         }
38514         
38515         
38516             
38517         // column width is fixed at the top - however if container width get's smaller we should
38518         // reduce it...
38519         
38520         // this bit calcs how man columns..
38521             
38522         var columnWidth = this.columnWidth += this.gutter;
38523       
38524         // calculate columns
38525         var containerWidth = this.containerWidth + this.gutter;
38526         
38527         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38528         // fix rounding errors, typically with gutters
38529         var excess = columnWidth - containerWidth % columnWidth;
38530         
38531         
38532         // if overshoot is less than a pixel, round up, otherwise floor it
38533         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38534         cols = Math[ mathMethod ]( cols );
38535         this.cols = Math.max( cols, 1 );
38536         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38537         
38538          // padding positioning..
38539         var totalColWidth = this.cols * this.columnWidth;
38540         var padavail = this.containerWidth - totalColWidth;
38541         // so for 2 columns - we need 3 'pads'
38542         
38543         var padNeeded = (1+this.cols) * this.padWidth;
38544         
38545         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38546         
38547         this.columnWidth += padExtra
38548         //this.padWidth = Math.floor(padavail /  ( this.cols));
38549         
38550         // adjust colum width so that padding is fixed??
38551         
38552         // we have 3 columns ... total = width * 3
38553         // we have X left over... that should be used by 
38554         
38555         //if (this.expandC) {
38556             
38557         //}
38558         
38559         
38560         
38561     },
38562     
38563     getContainerWidth : function()
38564     {
38565        /* // container is parent if fit width
38566         var container = this.isFitWidth ? this.element.parentNode : this.element;
38567         // check that this.size and size are there
38568         // IE8 triggers resize on body size change, so they might not be
38569         
38570         var size = getSize( container );  //FIXME
38571         this.containerWidth = size && size.innerWidth; //FIXME
38572         */
38573          
38574         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38575         
38576     },
38577     
38578     _getItemLayoutPosition : function( item )  // what is item?
38579     {
38580         // we resize the item to our columnWidth..
38581       
38582         item.setWidth(this.columnWidth);
38583         item.autoBoxAdjust  = false;
38584         
38585         var sz = item.getSize();
38586  
38587         // how many columns does this brick span
38588         var remainder = this.containerWidth % this.columnWidth;
38589         
38590         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38591         // round if off by 1 pixel, otherwise use ceil
38592         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38593         colSpan = Math.min( colSpan, this.cols );
38594         
38595         // normally this should be '1' as we dont' currently allow multi width columns..
38596         
38597         var colGroup = this._getColGroup( colSpan );
38598         // get the minimum Y value from the columns
38599         var minimumY = Math.min.apply( Math, colGroup );
38600         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38601         
38602         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38603          
38604         // position the brick
38605         var position = {
38606             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38607             y: this.currentSize.y + minimumY + this.padHeight
38608         };
38609         
38610         Roo.log(position);
38611         // apply setHeight to necessary columns
38612         var setHeight = minimumY + sz.height + this.padHeight;
38613         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38614         
38615         var setSpan = this.cols + 1 - colGroup.length;
38616         for ( var i = 0; i < setSpan; i++ ) {
38617           this.colYs[ shortColIndex + i ] = setHeight ;
38618         }
38619       
38620         return position;
38621     },
38622     
38623     /**
38624      * @param {Number} colSpan - number of columns the element spans
38625      * @returns {Array} colGroup
38626      */
38627     _getColGroup : function( colSpan )
38628     {
38629         if ( colSpan < 2 ) {
38630           // if brick spans only one column, use all the column Ys
38631           return this.colYs;
38632         }
38633       
38634         var colGroup = [];
38635         // how many different places could this brick fit horizontally
38636         var groupCount = this.cols + 1 - colSpan;
38637         // for each group potential horizontal position
38638         for ( var i = 0; i < groupCount; i++ ) {
38639           // make an array of colY values for that one group
38640           var groupColYs = this.colYs.slice( i, i + colSpan );
38641           // and get the max value of the array
38642           colGroup[i] = Math.max.apply( Math, groupColYs );
38643         }
38644         return colGroup;
38645     },
38646     /*
38647     _manageStamp : function( stamp )
38648     {
38649         var stampSize =  stamp.getSize();
38650         var offset = stamp.getBox();
38651         // get the columns that this stamp affects
38652         var firstX = this.isOriginLeft ? offset.x : offset.right;
38653         var lastX = firstX + stampSize.width;
38654         var firstCol = Math.floor( firstX / this.columnWidth );
38655         firstCol = Math.max( 0, firstCol );
38656         
38657         var lastCol = Math.floor( lastX / this.columnWidth );
38658         // lastCol should not go over if multiple of columnWidth #425
38659         lastCol -= lastX % this.columnWidth ? 0 : 1;
38660         lastCol = Math.min( this.cols - 1, lastCol );
38661         
38662         // set colYs to bottom of the stamp
38663         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38664             stampSize.height;
38665             
38666         for ( var i = firstCol; i <= lastCol; i++ ) {
38667           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38668         }
38669     },
38670     */
38671     
38672     _getContainerSize : function()
38673     {
38674         this.maxY = Math.max.apply( Math, this.colYs );
38675         var size = {
38676             height: this.maxY
38677         };
38678       
38679         if ( this.isFitWidth ) {
38680             size.width = this._getContainerFitWidth();
38681         }
38682       
38683         return size;
38684     },
38685     
38686     _getContainerFitWidth : function()
38687     {
38688         var unusedCols = 0;
38689         // count unused columns
38690         var i = this.cols;
38691         while ( --i ) {
38692           if ( this.colYs[i] !== 0 ) {
38693             break;
38694           }
38695           unusedCols++;
38696         }
38697         // fit container to columns that have been used
38698         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38699     },
38700     
38701     needsResizeLayout : function()
38702     {
38703         var previousWidth = this.containerWidth;
38704         this.getContainerWidth();
38705         return previousWidth !== this.containerWidth;
38706     }
38707  
38708 });
38709
38710  
38711
38712  /*
38713  * - LGPL
38714  *
38715  * element
38716  * 
38717  */
38718
38719 /**
38720  * @class Roo.bootstrap.MasonryBrick
38721  * @extends Roo.bootstrap.Component
38722  * Bootstrap MasonryBrick class
38723  * 
38724  * @constructor
38725  * Create a new MasonryBrick
38726  * @param {Object} config The config object
38727  */
38728
38729 Roo.bootstrap.MasonryBrick = function(config){
38730     
38731     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38732     
38733     Roo.bootstrap.MasonryBrick.register(this);
38734     
38735     this.addEvents({
38736         // raw events
38737         /**
38738          * @event click
38739          * When a MasonryBrick is clcik
38740          * @param {Roo.bootstrap.MasonryBrick} this
38741          * @param {Roo.EventObject} e
38742          */
38743         "click" : true
38744     });
38745 };
38746
38747 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38748     
38749     /**
38750      * @cfg {String} title
38751      */   
38752     title : '',
38753     /**
38754      * @cfg {String} html
38755      */   
38756     html : '',
38757     /**
38758      * @cfg {String} bgimage
38759      */   
38760     bgimage : '',
38761     /**
38762      * @cfg {String} videourl
38763      */   
38764     videourl : '',
38765     /**
38766      * @cfg {String} cls
38767      */   
38768     cls : '',
38769     /**
38770      * @cfg {String} href
38771      */   
38772     href : '',
38773     /**
38774      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38775      */   
38776     size : 'xs',
38777     
38778     /**
38779      * @cfg {String} placetitle (center|bottom)
38780      */   
38781     placetitle : '',
38782     
38783     /**
38784      * @cfg {Boolean} isFitContainer defalut true
38785      */   
38786     isFitContainer : true, 
38787     
38788     /**
38789      * @cfg {Boolean} preventDefault defalut false
38790      */   
38791     preventDefault : false, 
38792     
38793     /**
38794      * @cfg {Boolean} inverse defalut false
38795      */   
38796     maskInverse : false, 
38797     
38798     getAutoCreate : function()
38799     {
38800         if(!this.isFitContainer){
38801             return this.getSplitAutoCreate();
38802         }
38803         
38804         var cls = 'masonry-brick masonry-brick-full';
38805         
38806         if(this.href.length){
38807             cls += ' masonry-brick-link';
38808         }
38809         
38810         if(this.bgimage.length){
38811             cls += ' masonry-brick-image';
38812         }
38813         
38814         if(this.maskInverse){
38815             cls += ' mask-inverse';
38816         }
38817         
38818         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38819             cls += ' enable-mask';
38820         }
38821         
38822         if(this.size){
38823             cls += ' masonry-' + this.size + '-brick';
38824         }
38825         
38826         if(this.placetitle.length){
38827             
38828             switch (this.placetitle) {
38829                 case 'center' :
38830                     cls += ' masonry-center-title';
38831                     break;
38832                 case 'bottom' :
38833                     cls += ' masonry-bottom-title';
38834                     break;
38835                 default:
38836                     break;
38837             }
38838             
38839         } else {
38840             if(!this.html.length && !this.bgimage.length){
38841                 cls += ' masonry-center-title';
38842             }
38843
38844             if(!this.html.length && this.bgimage.length){
38845                 cls += ' masonry-bottom-title';
38846             }
38847         }
38848         
38849         if(this.cls){
38850             cls += ' ' + this.cls;
38851         }
38852         
38853         var cfg = {
38854             tag: (this.href.length) ? 'a' : 'div',
38855             cls: cls,
38856             cn: [
38857                 {
38858                     tag: 'div',
38859                     cls: 'masonry-brick-mask'
38860                 },
38861                 {
38862                     tag: 'div',
38863                     cls: 'masonry-brick-paragraph',
38864                     cn: []
38865                 }
38866             ]
38867         };
38868         
38869         if(this.href.length){
38870             cfg.href = this.href;
38871         }
38872         
38873         var cn = cfg.cn[1].cn;
38874         
38875         if(this.title.length){
38876             cn.push({
38877                 tag: 'h4',
38878                 cls: 'masonry-brick-title',
38879                 html: this.title
38880             });
38881         }
38882         
38883         if(this.html.length){
38884             cn.push({
38885                 tag: 'p',
38886                 cls: 'masonry-brick-text',
38887                 html: this.html
38888             });
38889         }
38890         
38891         if (!this.title.length && !this.html.length) {
38892             cfg.cn[1].cls += ' hide';
38893         }
38894         
38895         if(this.bgimage.length){
38896             cfg.cn.push({
38897                 tag: 'img',
38898                 cls: 'masonry-brick-image-view',
38899                 src: this.bgimage
38900             });
38901         }
38902         
38903         if(this.videourl.length){
38904             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38905             // youtube support only?
38906             cfg.cn.push({
38907                 tag: 'iframe',
38908                 cls: 'masonry-brick-image-view',
38909                 src: vurl,
38910                 frameborder : 0,
38911                 allowfullscreen : true
38912             });
38913         }
38914         
38915         return cfg;
38916         
38917     },
38918     
38919     getSplitAutoCreate : function()
38920     {
38921         var cls = 'masonry-brick masonry-brick-split';
38922         
38923         if(this.href.length){
38924             cls += ' masonry-brick-link';
38925         }
38926         
38927         if(this.bgimage.length){
38928             cls += ' masonry-brick-image';
38929         }
38930         
38931         if(this.size){
38932             cls += ' masonry-' + this.size + '-brick';
38933         }
38934         
38935         switch (this.placetitle) {
38936             case 'center' :
38937                 cls += ' masonry-center-title';
38938                 break;
38939             case 'bottom' :
38940                 cls += ' masonry-bottom-title';
38941                 break;
38942             default:
38943                 if(!this.bgimage.length){
38944                     cls += ' masonry-center-title';
38945                 }
38946
38947                 if(this.bgimage.length){
38948                     cls += ' masonry-bottom-title';
38949                 }
38950                 break;
38951         }
38952         
38953         if(this.cls){
38954             cls += ' ' + this.cls;
38955         }
38956         
38957         var cfg = {
38958             tag: (this.href.length) ? 'a' : 'div',
38959             cls: cls,
38960             cn: [
38961                 {
38962                     tag: 'div',
38963                     cls: 'masonry-brick-split-head',
38964                     cn: [
38965                         {
38966                             tag: 'div',
38967                             cls: 'masonry-brick-paragraph',
38968                             cn: []
38969                         }
38970                     ]
38971                 },
38972                 {
38973                     tag: 'div',
38974                     cls: 'masonry-brick-split-body',
38975                     cn: []
38976                 }
38977             ]
38978         };
38979         
38980         if(this.href.length){
38981             cfg.href = this.href;
38982         }
38983         
38984         if(this.title.length){
38985             cfg.cn[0].cn[0].cn.push({
38986                 tag: 'h4',
38987                 cls: 'masonry-brick-title',
38988                 html: this.title
38989             });
38990         }
38991         
38992         if(this.html.length){
38993             cfg.cn[1].cn.push({
38994                 tag: 'p',
38995                 cls: 'masonry-brick-text',
38996                 html: this.html
38997             });
38998         }
38999
39000         if(this.bgimage.length){
39001             cfg.cn[0].cn.push({
39002                 tag: 'img',
39003                 cls: 'masonry-brick-image-view',
39004                 src: this.bgimage
39005             });
39006         }
39007         
39008         if(this.videourl.length){
39009             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39010             // youtube support only?
39011             cfg.cn[0].cn.cn.push({
39012                 tag: 'iframe',
39013                 cls: 'masonry-brick-image-view',
39014                 src: vurl,
39015                 frameborder : 0,
39016                 allowfullscreen : true
39017             });
39018         }
39019         
39020         return cfg;
39021     },
39022     
39023     initEvents: function() 
39024     {
39025         switch (this.size) {
39026             case 'xs' :
39027                 this.x = 1;
39028                 this.y = 1;
39029                 break;
39030             case 'sm' :
39031                 this.x = 2;
39032                 this.y = 2;
39033                 break;
39034             case 'md' :
39035             case 'md-left' :
39036             case 'md-right' :
39037                 this.x = 3;
39038                 this.y = 3;
39039                 break;
39040             case 'tall' :
39041                 this.x = 2;
39042                 this.y = 3;
39043                 break;
39044             case 'wide' :
39045                 this.x = 3;
39046                 this.y = 2;
39047                 break;
39048             case 'wide-thin' :
39049                 this.x = 3;
39050                 this.y = 1;
39051                 break;
39052                         
39053             default :
39054                 break;
39055         }
39056         
39057         if(Roo.isTouch){
39058             this.el.on('touchstart', this.onTouchStart, this);
39059             this.el.on('touchmove', this.onTouchMove, this);
39060             this.el.on('touchend', this.onTouchEnd, this);
39061             this.el.on('contextmenu', this.onContextMenu, this);
39062         } else {
39063             this.el.on('mouseenter'  ,this.enter, this);
39064             this.el.on('mouseleave', this.leave, this);
39065             this.el.on('click', this.onClick, this);
39066         }
39067         
39068         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39069             this.parent().bricks.push(this);   
39070         }
39071         
39072     },
39073     
39074     onClick: function(e, el)
39075     {
39076         var time = this.endTimer - this.startTimer;
39077         // Roo.log(e.preventDefault());
39078         if(Roo.isTouch){
39079             if(time > 1000){
39080                 e.preventDefault();
39081                 return;
39082             }
39083         }
39084         
39085         if(!this.preventDefault){
39086             return;
39087         }
39088         
39089         e.preventDefault();
39090         
39091         if (this.activeClass != '') {
39092             this.selectBrick();
39093         }
39094         
39095         this.fireEvent('click', this, e);
39096     },
39097     
39098     enter: function(e, el)
39099     {
39100         e.preventDefault();
39101         
39102         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39103             return;
39104         }
39105         
39106         if(this.bgimage.length && this.html.length){
39107             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39108         }
39109     },
39110     
39111     leave: function(e, el)
39112     {
39113         e.preventDefault();
39114         
39115         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
39116             return;
39117         }
39118         
39119         if(this.bgimage.length && this.html.length){
39120             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39121         }
39122     },
39123     
39124     onTouchStart: function(e, el)
39125     {
39126 //        e.preventDefault();
39127         
39128         this.touchmoved = false;
39129         
39130         if(!this.isFitContainer){
39131             return;
39132         }
39133         
39134         if(!this.bgimage.length || !this.html.length){
39135             return;
39136         }
39137         
39138         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39139         
39140         this.timer = new Date().getTime();
39141         
39142     },
39143     
39144     onTouchMove: function(e, el)
39145     {
39146         this.touchmoved = true;
39147     },
39148     
39149     onContextMenu : function(e,el)
39150     {
39151         e.preventDefault();
39152         e.stopPropagation();
39153         return false;
39154     },
39155     
39156     onTouchEnd: function(e, el)
39157     {
39158 //        e.preventDefault();
39159         
39160         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39161         
39162             this.leave(e,el);
39163             
39164             return;
39165         }
39166         
39167         if(!this.bgimage.length || !this.html.length){
39168             
39169             if(this.href.length){
39170                 window.location.href = this.href;
39171             }
39172             
39173             return;
39174         }
39175         
39176         if(!this.isFitContainer){
39177             return;
39178         }
39179         
39180         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39181         
39182         window.location.href = this.href;
39183     },
39184     
39185     //selection on single brick only
39186     selectBrick : function() {
39187         
39188         if (!this.parentId) {
39189             return;
39190         }
39191         
39192         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39193         var index = m.selectedBrick.indexOf(this.id);
39194         
39195         if ( index > -1) {
39196             m.selectedBrick.splice(index,1);
39197             this.el.removeClass(this.activeClass);
39198             return;
39199         }
39200         
39201         for(var i = 0; i < m.selectedBrick.length; i++) {
39202             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39203             b.el.removeClass(b.activeClass);
39204         }
39205         
39206         m.selectedBrick = [];
39207         
39208         m.selectedBrick.push(this.id);
39209         this.el.addClass(this.activeClass);
39210         return;
39211     },
39212     
39213     isSelected : function(){
39214         return this.el.hasClass(this.activeClass);
39215         
39216     }
39217 });
39218
39219 Roo.apply(Roo.bootstrap.MasonryBrick, {
39220     
39221     //groups: {},
39222     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39223      /**
39224     * register a Masonry Brick
39225     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39226     */
39227     
39228     register : function(brick)
39229     {
39230         //this.groups[brick.id] = brick;
39231         this.groups.add(brick.id, brick);
39232     },
39233     /**
39234     * fetch a  masonry brick based on the masonry brick ID
39235     * @param {string} the masonry brick to add
39236     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39237     */
39238     
39239     get: function(brick_id) 
39240     {
39241         // if (typeof(this.groups[brick_id]) == 'undefined') {
39242         //     return false;
39243         // }
39244         // return this.groups[brick_id] ;
39245         
39246         if(this.groups.key(brick_id)) {
39247             return this.groups.key(brick_id);
39248         }
39249         
39250         return false;
39251     }
39252     
39253     
39254     
39255 });
39256
39257  /*
39258  * - LGPL
39259  *
39260  * element
39261  * 
39262  */
39263
39264 /**
39265  * @class Roo.bootstrap.Brick
39266  * @extends Roo.bootstrap.Component
39267  * Bootstrap Brick class
39268  * 
39269  * @constructor
39270  * Create a new Brick
39271  * @param {Object} config The config object
39272  */
39273
39274 Roo.bootstrap.Brick = function(config){
39275     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39276     
39277     this.addEvents({
39278         // raw events
39279         /**
39280          * @event click
39281          * When a Brick is click
39282          * @param {Roo.bootstrap.Brick} this
39283          * @param {Roo.EventObject} e
39284          */
39285         "click" : true
39286     });
39287 };
39288
39289 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39290     
39291     /**
39292      * @cfg {String} title
39293      */   
39294     title : '',
39295     /**
39296      * @cfg {String} html
39297      */   
39298     html : '',
39299     /**
39300      * @cfg {String} bgimage
39301      */   
39302     bgimage : '',
39303     /**
39304      * @cfg {String} cls
39305      */   
39306     cls : '',
39307     /**
39308      * @cfg {String} href
39309      */   
39310     href : '',
39311     /**
39312      * @cfg {String} video
39313      */   
39314     video : '',
39315     /**
39316      * @cfg {Boolean} square
39317      */   
39318     square : true,
39319     
39320     getAutoCreate : function()
39321     {
39322         var cls = 'roo-brick';
39323         
39324         if(this.href.length){
39325             cls += ' roo-brick-link';
39326         }
39327         
39328         if(this.bgimage.length){
39329             cls += ' roo-brick-image';
39330         }
39331         
39332         if(!this.html.length && !this.bgimage.length){
39333             cls += ' roo-brick-center-title';
39334         }
39335         
39336         if(!this.html.length && this.bgimage.length){
39337             cls += ' roo-brick-bottom-title';
39338         }
39339         
39340         if(this.cls){
39341             cls += ' ' + this.cls;
39342         }
39343         
39344         var cfg = {
39345             tag: (this.href.length) ? 'a' : 'div',
39346             cls: cls,
39347             cn: [
39348                 {
39349                     tag: 'div',
39350                     cls: 'roo-brick-paragraph',
39351                     cn: []
39352                 }
39353             ]
39354         };
39355         
39356         if(this.href.length){
39357             cfg.href = this.href;
39358         }
39359         
39360         var cn = cfg.cn[0].cn;
39361         
39362         if(this.title.length){
39363             cn.push({
39364                 tag: 'h4',
39365                 cls: 'roo-brick-title',
39366                 html: this.title
39367             });
39368         }
39369         
39370         if(this.html.length){
39371             cn.push({
39372                 tag: 'p',
39373                 cls: 'roo-brick-text',
39374                 html: this.html
39375             });
39376         } else {
39377             cn.cls += ' hide';
39378         }
39379         
39380         if(this.bgimage.length){
39381             cfg.cn.push({
39382                 tag: 'img',
39383                 cls: 'roo-brick-image-view',
39384                 src: this.bgimage
39385             });
39386         }
39387         
39388         return cfg;
39389     },
39390     
39391     initEvents: function() 
39392     {
39393         if(this.title.length || this.html.length){
39394             this.el.on('mouseenter'  ,this.enter, this);
39395             this.el.on('mouseleave', this.leave, this);
39396         }
39397         
39398         Roo.EventManager.onWindowResize(this.resize, this); 
39399         
39400         if(this.bgimage.length){
39401             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39402             this.imageEl.on('load', this.onImageLoad, this);
39403             return;
39404         }
39405         
39406         this.resize();
39407     },
39408     
39409     onImageLoad : function()
39410     {
39411         this.resize();
39412     },
39413     
39414     resize : function()
39415     {
39416         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39417         
39418         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39419         
39420         if(this.bgimage.length){
39421             var image = this.el.select('.roo-brick-image-view', true).first();
39422             
39423             image.setWidth(paragraph.getWidth());
39424             
39425             if(this.square){
39426                 image.setHeight(paragraph.getWidth());
39427             }
39428             
39429             this.el.setHeight(image.getHeight());
39430             paragraph.setHeight(image.getHeight());
39431             
39432         }
39433         
39434     },
39435     
39436     enter: function(e, el)
39437     {
39438         e.preventDefault();
39439         
39440         if(this.bgimage.length){
39441             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39442             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39443         }
39444     },
39445     
39446     leave: function(e, el)
39447     {
39448         e.preventDefault();
39449         
39450         if(this.bgimage.length){
39451             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39452             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39453         }
39454     }
39455     
39456 });
39457
39458  
39459
39460  /*
39461  * - LGPL
39462  *
39463  * Number field 
39464  */
39465
39466 /**
39467  * @class Roo.bootstrap.form.NumberField
39468  * @extends Roo.bootstrap.form.Input
39469  * Bootstrap NumberField class
39470  * 
39471  * 
39472  * 
39473  * 
39474  * @constructor
39475  * Create a new NumberField
39476  * @param {Object} config The config object
39477  */
39478
39479 Roo.bootstrap.form.NumberField = function(config){
39480     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39481 };
39482
39483 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39484     
39485     /**
39486      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39487      */
39488     allowDecimals : true,
39489     /**
39490      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39491      */
39492     decimalSeparator : ".",
39493     /**
39494      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39495      */
39496     decimalPrecision : 2,
39497     /**
39498      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39499      */
39500     allowNegative : true,
39501     
39502     /**
39503      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39504      */
39505     allowZero: true,
39506     /**
39507      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39508      */
39509     minValue : Number.NEGATIVE_INFINITY,
39510     /**
39511      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39512      */
39513     maxValue : Number.MAX_VALUE,
39514     /**
39515      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39516      */
39517     minText : "The minimum value for this field is {0}",
39518     /**
39519      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39520      */
39521     maxText : "The maximum value for this field is {0}",
39522     /**
39523      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39524      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39525      */
39526     nanText : "{0} is not a valid number",
39527     /**
39528      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39529      */
39530     thousandsDelimiter : false,
39531     /**
39532      * @cfg {String} valueAlign alignment of value
39533      */
39534     valueAlign : "left",
39535
39536     getAutoCreate : function()
39537     {
39538         var hiddenInput = {
39539             tag: 'input',
39540             type: 'hidden',
39541             id: Roo.id(),
39542             cls: 'hidden-number-input'
39543         };
39544         
39545         if (this.name) {
39546             hiddenInput.name = this.name;
39547         }
39548         
39549         this.name = '';
39550         
39551         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39552         
39553         this.name = hiddenInput.name;
39554         
39555         if(cfg.cn.length > 0) {
39556             cfg.cn.push(hiddenInput);
39557         }
39558         
39559         return cfg;
39560     },
39561
39562     // private
39563     initEvents : function()
39564     {   
39565         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39566         
39567         var allowed = "0123456789";
39568         
39569         if(this.allowDecimals){
39570             allowed += this.decimalSeparator;
39571         }
39572         
39573         if(this.allowNegative){
39574             allowed += "-";
39575         }
39576         
39577         if(this.thousandsDelimiter) {
39578             allowed += ",";
39579         }
39580         
39581         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39582         
39583         var keyPress = function(e){
39584             
39585             var k = e.getKey();
39586             
39587             var c = e.getCharCode();
39588             
39589             if(
39590                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39591                     allowed.indexOf(String.fromCharCode(c)) === -1
39592             ){
39593                 e.stopEvent();
39594                 return;
39595             }
39596             
39597             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39598                 return;
39599             }
39600             
39601             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39602                 e.stopEvent();
39603             }
39604         };
39605         
39606         this.el.on("keypress", keyPress, this);
39607     },
39608     
39609     validateValue : function(value)
39610     {
39611         
39612         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39613             return false;
39614         }
39615         
39616         var num = this.parseValue(value);
39617         
39618         if(isNaN(num)){
39619             this.markInvalid(String.format(this.nanText, value));
39620             return false;
39621         }
39622         
39623         if(num < this.minValue){
39624             this.markInvalid(String.format(this.minText, this.minValue));
39625             return false;
39626         }
39627         
39628         if(num > this.maxValue){
39629             this.markInvalid(String.format(this.maxText, this.maxValue));
39630             return false;
39631         }
39632         
39633         return true;
39634     },
39635
39636     getValue : function()
39637     {
39638         var v = this.hiddenEl().getValue();
39639         
39640         return this.fixPrecision(this.parseValue(v));
39641     },
39642
39643     parseValue : function(value)
39644     {
39645         if(this.thousandsDelimiter) {
39646             value += "";
39647             r = new RegExp(",", "g");
39648             value = value.replace(r, "");
39649         }
39650         
39651         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39652         return isNaN(value) ? '' : value;
39653     },
39654
39655     fixPrecision : function(value)
39656     {
39657         if(this.thousandsDelimiter) {
39658             value += "";
39659             r = new RegExp(",", "g");
39660             value = value.replace(r, "");
39661         }
39662         
39663         var nan = isNaN(value);
39664         
39665         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39666             return nan ? '' : value;
39667         }
39668         return parseFloat(value).toFixed(this.decimalPrecision);
39669     },
39670
39671     setValue : function(v)
39672     {
39673         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39674         
39675         this.value = v;
39676         
39677         if(this.rendered){
39678             
39679             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39680             
39681             this.inputEl().dom.value = (v == '') ? '' :
39682                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39683             
39684             if(!this.allowZero && v === '0') {
39685                 this.hiddenEl().dom.value = '';
39686                 this.inputEl().dom.value = '';
39687             }
39688             
39689             this.validate();
39690         }
39691     },
39692
39693     decimalPrecisionFcn : function(v)
39694     {
39695         return Math.floor(v);
39696     },
39697
39698     beforeBlur : function()
39699     {
39700         var v = this.parseValue(this.getRawValue());
39701         
39702         if(v || v === 0 || v === ''){
39703             this.setValue(v);
39704         }
39705     },
39706     
39707     hiddenEl : function()
39708     {
39709         return this.el.select('input.hidden-number-input',true).first();
39710     }
39711     
39712 });
39713
39714  
39715
39716 /*
39717 * Licence: LGPL
39718 */
39719
39720 /**
39721  * @class Roo.bootstrap.DocumentSlider
39722  * @extends Roo.bootstrap.Component
39723  * Bootstrap DocumentSlider class
39724  * 
39725  * @constructor
39726  * Create a new DocumentViewer
39727  * @param {Object} config The config object
39728  */
39729
39730 Roo.bootstrap.DocumentSlider = function(config){
39731     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39732     
39733     this.files = [];
39734     
39735     this.addEvents({
39736         /**
39737          * @event initial
39738          * Fire after initEvent
39739          * @param {Roo.bootstrap.DocumentSlider} this
39740          */
39741         "initial" : true,
39742         /**
39743          * @event update
39744          * Fire after update
39745          * @param {Roo.bootstrap.DocumentSlider} this
39746          */
39747         "update" : true,
39748         /**
39749          * @event click
39750          * Fire after click
39751          * @param {Roo.bootstrap.DocumentSlider} this
39752          */
39753         "click" : true
39754     });
39755 };
39756
39757 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39758     
39759     files : false,
39760     
39761     indicator : 0,
39762     
39763     getAutoCreate : function()
39764     {
39765         var cfg = {
39766             tag : 'div',
39767             cls : 'roo-document-slider',
39768             cn : [
39769                 {
39770                     tag : 'div',
39771                     cls : 'roo-document-slider-header',
39772                     cn : [
39773                         {
39774                             tag : 'div',
39775                             cls : 'roo-document-slider-header-title'
39776                         }
39777                     ]
39778                 },
39779                 {
39780                     tag : 'div',
39781                     cls : 'roo-document-slider-body',
39782                     cn : [
39783                         {
39784                             tag : 'div',
39785                             cls : 'roo-document-slider-prev',
39786                             cn : [
39787                                 {
39788                                     tag : 'i',
39789                                     cls : 'fa fa-chevron-left'
39790                                 }
39791                             ]
39792                         },
39793                         {
39794                             tag : 'div',
39795                             cls : 'roo-document-slider-thumb',
39796                             cn : [
39797                                 {
39798                                     tag : 'img',
39799                                     cls : 'roo-document-slider-image'
39800                                 }
39801                             ]
39802                         },
39803                         {
39804                             tag : 'div',
39805                             cls : 'roo-document-slider-next',
39806                             cn : [
39807                                 {
39808                                     tag : 'i',
39809                                     cls : 'fa fa-chevron-right'
39810                                 }
39811                             ]
39812                         }
39813                     ]
39814                 }
39815             ]
39816         };
39817         
39818         return cfg;
39819     },
39820     
39821     initEvents : function()
39822     {
39823         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39824         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39825         
39826         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39827         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39828         
39829         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39830         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39831         
39832         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39833         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39834         
39835         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39836         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39837         
39838         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39839         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39840         
39841         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39842         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39843         
39844         this.thumbEl.on('click', this.onClick, this);
39845         
39846         this.prevIndicator.on('click', this.prev, this);
39847         
39848         this.nextIndicator.on('click', this.next, this);
39849         
39850     },
39851     
39852     initial : function()
39853     {
39854         if(this.files.length){
39855             this.indicator = 1;
39856             this.update()
39857         }
39858         
39859         this.fireEvent('initial', this);
39860     },
39861     
39862     update : function()
39863     {
39864         this.imageEl.attr('src', this.files[this.indicator - 1]);
39865         
39866         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39867         
39868         this.prevIndicator.show();
39869         
39870         if(this.indicator == 1){
39871             this.prevIndicator.hide();
39872         }
39873         
39874         this.nextIndicator.show();
39875         
39876         if(this.indicator == this.files.length){
39877             this.nextIndicator.hide();
39878         }
39879         
39880         this.thumbEl.scrollTo('top');
39881         
39882         this.fireEvent('update', this);
39883     },
39884     
39885     onClick : function(e)
39886     {
39887         e.preventDefault();
39888         
39889         this.fireEvent('click', this);
39890     },
39891     
39892     prev : function(e)
39893     {
39894         e.preventDefault();
39895         
39896         this.indicator = Math.max(1, this.indicator - 1);
39897         
39898         this.update();
39899     },
39900     
39901     next : function(e)
39902     {
39903         e.preventDefault();
39904         
39905         this.indicator = Math.min(this.files.length, this.indicator + 1);
39906         
39907         this.update();
39908     }
39909 });
39910 /*
39911  * - LGPL
39912  *
39913  * RadioSet
39914  *
39915  *
39916  */
39917
39918 /**
39919  * @class Roo.bootstrap.form.RadioSet
39920  * @extends Roo.bootstrap.form.Input
39921  * @children Roo.bootstrap.form.Radio
39922  * Bootstrap RadioSet class
39923  * @cfg {String} indicatorpos (left|right) default left
39924  * @cfg {Boolean} inline (true|false) inline the element (default true)
39925  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39926  * @constructor
39927  * Create a new RadioSet
39928  * @param {Object} config The config object
39929  */
39930
39931 Roo.bootstrap.form.RadioSet = function(config){
39932     
39933     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39934     
39935     this.radioes = [];
39936     
39937     Roo.bootstrap.form.RadioSet.register(this);
39938     
39939     this.addEvents({
39940         /**
39941         * @event check
39942         * Fires when the element is checked or unchecked.
39943         * @param {Roo.bootstrap.form.RadioSet} this This radio
39944         * @param {Roo.bootstrap.form.Radio} item The checked item
39945         */
39946        check : true,
39947        /**
39948         * @event click
39949         * Fires when the element is click.
39950         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39951         * @param {Roo.bootstrap.form.Radio} item The checked item
39952         * @param {Roo.EventObject} e The event object
39953         */
39954        click : true
39955     });
39956     
39957 };
39958
39959 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39960
39961     radioes : false,
39962     
39963     inline : true,
39964     
39965     weight : '',
39966     
39967     indicatorpos : 'left',
39968     
39969     getAutoCreate : function()
39970     {
39971         var label = {
39972             tag : 'label',
39973             cls : 'roo-radio-set-label',
39974             cn : [
39975                 {
39976                     tag : 'span',
39977                     html : this.fieldLabel
39978                 }
39979             ]
39980         };
39981         if (Roo.bootstrap.version == 3) {
39982             
39983             
39984             if(this.indicatorpos == 'left'){
39985                 label.cn.unshift({
39986                     tag : 'i',
39987                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39988                     tooltip : 'This field is required'
39989                 });
39990             } else {
39991                 label.cn.push({
39992                     tag : 'i',
39993                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39994                     tooltip : 'This field is required'
39995                 });
39996             }
39997         }
39998         var items = {
39999             tag : 'div',
40000             cls : 'roo-radio-set-items'
40001         };
40002         
40003         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40004         
40005         if (align === 'left' && this.fieldLabel.length) {
40006             
40007             items = {
40008                 cls : "roo-radio-set-right", 
40009                 cn: [
40010                     items
40011                 ]
40012             };
40013             
40014             if(this.labelWidth > 12){
40015                 label.style = "width: " + this.labelWidth + 'px';
40016             }
40017             
40018             if(this.labelWidth < 13 && this.labelmd == 0){
40019                 this.labelmd = this.labelWidth;
40020             }
40021             
40022             if(this.labellg > 0){
40023                 label.cls += ' col-lg-' + this.labellg;
40024                 items.cls += ' col-lg-' + (12 - this.labellg);
40025             }
40026             
40027             if(this.labelmd > 0){
40028                 label.cls += ' col-md-' + this.labelmd;
40029                 items.cls += ' col-md-' + (12 - this.labelmd);
40030             }
40031             
40032             if(this.labelsm > 0){
40033                 label.cls += ' col-sm-' + this.labelsm;
40034                 items.cls += ' col-sm-' + (12 - this.labelsm);
40035             }
40036             
40037             if(this.labelxs > 0){
40038                 label.cls += ' col-xs-' + this.labelxs;
40039                 items.cls += ' col-xs-' + (12 - this.labelxs);
40040             }
40041         }
40042         
40043         var cfg = {
40044             tag : 'div',
40045             cls : 'roo-radio-set',
40046             cn : [
40047                 {
40048                     tag : 'input',
40049                     cls : 'roo-radio-set-input',
40050                     type : 'hidden',
40051                     name : this.name,
40052                     value : this.value ? this.value :  ''
40053                 },
40054                 label,
40055                 items
40056             ]
40057         };
40058         
40059         if(this.weight.length){
40060             cfg.cls += ' roo-radio-' + this.weight;
40061         }
40062         
40063         if(this.inline) {
40064             cfg.cls += ' roo-radio-set-inline';
40065         }
40066         
40067         var settings=this;
40068         ['xs','sm','md','lg'].map(function(size){
40069             if (settings[size]) {
40070                 cfg.cls += ' col-' + size + '-' + settings[size];
40071             }
40072         });
40073         
40074         return cfg;
40075         
40076     },
40077
40078     initEvents : function()
40079     {
40080         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40081         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40082         
40083         if(!this.fieldLabel.length){
40084             this.labelEl.hide();
40085         }
40086         
40087         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40088         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40089         
40090         this.indicator = this.indicatorEl();
40091         
40092         if(this.indicator){
40093             this.indicator.addClass('invisible');
40094         }
40095         
40096         this.originalValue = this.getValue();
40097         
40098     },
40099     
40100     inputEl: function ()
40101     {
40102         return this.el.select('.roo-radio-set-input', true).first();
40103     },
40104     
40105     getChildContainer : function()
40106     {
40107         return this.itemsEl;
40108     },
40109     
40110     register : function(item)
40111     {
40112         this.radioes.push(item);
40113         
40114     },
40115     
40116     validate : function()
40117     {   
40118         if(this.getVisibilityEl().hasClass('hidden')){
40119             return true;
40120         }
40121         
40122         var valid = false;
40123         
40124         Roo.each(this.radioes, function(i){
40125             if(!i.checked){
40126                 return;
40127             }
40128             
40129             valid = true;
40130             return false;
40131         });
40132         
40133         if(this.allowBlank) {
40134             return true;
40135         }
40136         
40137         if(this.disabled || valid){
40138             this.markValid();
40139             return true;
40140         }
40141         
40142         this.markInvalid();
40143         return false;
40144         
40145     },
40146     
40147     markValid : function()
40148     {
40149         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40150             this.indicatorEl().removeClass('visible');
40151             this.indicatorEl().addClass('invisible');
40152         }
40153         
40154         
40155         if (Roo.bootstrap.version == 3) {
40156             this.el.removeClass([this.invalidClass, this.validClass]);
40157             this.el.addClass(this.validClass);
40158         } else {
40159             this.el.removeClass(['is-invalid','is-valid']);
40160             this.el.addClass(['is-valid']);
40161         }
40162         this.fireEvent('valid', this);
40163     },
40164     
40165     markInvalid : function(msg)
40166     {
40167         if(this.allowBlank || this.disabled){
40168             return;
40169         }
40170         
40171         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40172             this.indicatorEl().removeClass('invisible');
40173             this.indicatorEl().addClass('visible');
40174         }
40175         if (Roo.bootstrap.version == 3) {
40176             this.el.removeClass([this.invalidClass, this.validClass]);
40177             this.el.addClass(this.invalidClass);
40178         } else {
40179             this.el.removeClass(['is-invalid','is-valid']);
40180             this.el.addClass(['is-invalid']);
40181         }
40182         
40183         this.fireEvent('invalid', this, msg);
40184         
40185     },
40186     
40187     setValue : function(v, suppressEvent)
40188     {   
40189         if(this.value === v){
40190             return;
40191         }
40192         
40193         this.value = v;
40194         
40195         if(this.rendered){
40196             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40197         }
40198         
40199         Roo.each(this.radioes, function(i){
40200             i.checked = false;
40201             i.el.removeClass('checked');
40202         });
40203         
40204         Roo.each(this.radioes, function(i){
40205             
40206             if(i.value === v || i.value.toString() === v.toString()){
40207                 i.checked = true;
40208                 i.el.addClass('checked');
40209                 
40210                 if(suppressEvent !== true){
40211                     this.fireEvent('check', this, i);
40212                 }
40213                 
40214                 return false;
40215             }
40216             
40217         }, this);
40218         
40219         this.validate();
40220     },
40221     
40222     clearInvalid : function(){
40223         
40224         if(!this.el || this.preventMark){
40225             return;
40226         }
40227         
40228         this.el.removeClass([this.invalidClass]);
40229         
40230         this.fireEvent('valid', this);
40231     }
40232     
40233 });
40234
40235 Roo.apply(Roo.bootstrap.form.RadioSet, {
40236     
40237     groups: {},
40238     
40239     register : function(set)
40240     {
40241         this.groups[set.name] = set;
40242     },
40243     
40244     get: function(name) 
40245     {
40246         if (typeof(this.groups[name]) == 'undefined') {
40247             return false;
40248         }
40249         
40250         return this.groups[name] ;
40251     }
40252     
40253 });
40254 /*
40255  * Based on:
40256  * Ext JS Library 1.1.1
40257  * Copyright(c) 2006-2007, Ext JS, LLC.
40258  *
40259  * Originally Released Under LGPL - original licence link has changed is not relivant.
40260  *
40261  * Fork - LGPL
40262  * <script type="text/javascript">
40263  */
40264
40265
40266 /**
40267  * @class Roo.bootstrap.SplitBar
40268  * @extends Roo.util.Observable
40269  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40270  * <br><br>
40271  * Usage:
40272  * <pre><code>
40273 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40274                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40275 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40276 split.minSize = 100;
40277 split.maxSize = 600;
40278 split.animate = true;
40279 split.on('moved', splitterMoved);
40280 </code></pre>
40281  * @constructor
40282  * Create a new SplitBar
40283  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40284  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40285  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40286  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40287                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40288                         position of the SplitBar).
40289  */
40290 Roo.bootstrap.SplitBar = function(cfg){
40291     
40292     /** @private */
40293     
40294     //{
40295     //  dragElement : elm
40296     //  resizingElement: el,
40297         // optional..
40298     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40299     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40300         // existingProxy ???
40301     //}
40302     
40303     this.el = Roo.get(cfg.dragElement, true);
40304     this.el.dom.unselectable = "on";
40305     /** @private */
40306     this.resizingEl = Roo.get(cfg.resizingElement, true);
40307
40308     /**
40309      * @private
40310      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40311      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40312      * @type Number
40313      */
40314     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40315     
40316     /**
40317      * The minimum size of the resizing element. (Defaults to 0)
40318      * @type Number
40319      */
40320     this.minSize = 0;
40321     
40322     /**
40323      * The maximum size of the resizing element. (Defaults to 2000)
40324      * @type Number
40325      */
40326     this.maxSize = 2000;
40327     
40328     /**
40329      * Whether to animate the transition to the new size
40330      * @type Boolean
40331      */
40332     this.animate = false;
40333     
40334     /**
40335      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40336      * @type Boolean
40337      */
40338     this.useShim = false;
40339     
40340     /** @private */
40341     this.shim = null;
40342     
40343     if(!cfg.existingProxy){
40344         /** @private */
40345         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40346     }else{
40347         this.proxy = Roo.get(cfg.existingProxy).dom;
40348     }
40349     /** @private */
40350     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40351     
40352     /** @private */
40353     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40354     
40355     /** @private */
40356     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40357     
40358     /** @private */
40359     this.dragSpecs = {};
40360     
40361     /**
40362      * @private The adapter to use to positon and resize elements
40363      */
40364     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40365     this.adapter.init(this);
40366     
40367     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40368         /** @private */
40369         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40370         this.el.addClass("roo-splitbar-h");
40371     }else{
40372         /** @private */
40373         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40374         this.el.addClass("roo-splitbar-v");
40375     }
40376     
40377     this.addEvents({
40378         /**
40379          * @event resize
40380          * Fires when the splitter is moved (alias for {@link #event-moved})
40381          * @param {Roo.bootstrap.SplitBar} this
40382          * @param {Number} newSize the new width or height
40383          */
40384         "resize" : true,
40385         /**
40386          * @event moved
40387          * Fires when the splitter is moved
40388          * @param {Roo.bootstrap.SplitBar} this
40389          * @param {Number} newSize the new width or height
40390          */
40391         "moved" : true,
40392         /**
40393          * @event beforeresize
40394          * Fires before the splitter is dragged
40395          * @param {Roo.bootstrap.SplitBar} this
40396          */
40397         "beforeresize" : true,
40398
40399         "beforeapply" : true
40400     });
40401
40402     Roo.util.Observable.call(this);
40403 };
40404
40405 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40406     onStartProxyDrag : function(x, y){
40407         this.fireEvent("beforeresize", this);
40408         if(!this.overlay){
40409             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40410             o.unselectable();
40411             o.enableDisplayMode("block");
40412             // all splitbars share the same overlay
40413             Roo.bootstrap.SplitBar.prototype.overlay = o;
40414         }
40415         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40416         this.overlay.show();
40417         Roo.get(this.proxy).setDisplayed("block");
40418         var size = this.adapter.getElementSize(this);
40419         this.activeMinSize = this.getMinimumSize();;
40420         this.activeMaxSize = this.getMaximumSize();;
40421         var c1 = size - this.activeMinSize;
40422         var c2 = Math.max(this.activeMaxSize - size, 0);
40423         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40424             this.dd.resetConstraints();
40425             this.dd.setXConstraint(
40426                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40427                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40428             );
40429             this.dd.setYConstraint(0, 0);
40430         }else{
40431             this.dd.resetConstraints();
40432             this.dd.setXConstraint(0, 0);
40433             this.dd.setYConstraint(
40434                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40435                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40436             );
40437          }
40438         this.dragSpecs.startSize = size;
40439         this.dragSpecs.startPoint = [x, y];
40440         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40441     },
40442     
40443     /** 
40444      * @private Called after the drag operation by the DDProxy
40445      */
40446     onEndProxyDrag : function(e){
40447         Roo.get(this.proxy).setDisplayed(false);
40448         var endPoint = Roo.lib.Event.getXY(e);
40449         if(this.overlay){
40450             this.overlay.hide();
40451         }
40452         var newSize;
40453         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40454             newSize = this.dragSpecs.startSize + 
40455                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40456                     endPoint[0] - this.dragSpecs.startPoint[0] :
40457                     this.dragSpecs.startPoint[0] - endPoint[0]
40458                 );
40459         }else{
40460             newSize = this.dragSpecs.startSize + 
40461                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40462                     endPoint[1] - this.dragSpecs.startPoint[1] :
40463                     this.dragSpecs.startPoint[1] - endPoint[1]
40464                 );
40465         }
40466         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40467         if(newSize != this.dragSpecs.startSize){
40468             if(this.fireEvent('beforeapply', this, newSize) !== false){
40469                 this.adapter.setElementSize(this, newSize);
40470                 this.fireEvent("moved", this, newSize);
40471                 this.fireEvent("resize", this, newSize);
40472             }
40473         }
40474     },
40475     
40476     /**
40477      * Get the adapter this SplitBar uses
40478      * @return The adapter object
40479      */
40480     getAdapter : function(){
40481         return this.adapter;
40482     },
40483     
40484     /**
40485      * Set the adapter this SplitBar uses
40486      * @param {Object} adapter A SplitBar adapter object
40487      */
40488     setAdapter : function(adapter){
40489         this.adapter = adapter;
40490         this.adapter.init(this);
40491     },
40492     
40493     /**
40494      * Gets the minimum size for the resizing element
40495      * @return {Number} The minimum size
40496      */
40497     getMinimumSize : function(){
40498         return this.minSize;
40499     },
40500     
40501     /**
40502      * Sets the minimum size for the resizing element
40503      * @param {Number} minSize The minimum size
40504      */
40505     setMinimumSize : function(minSize){
40506         this.minSize = minSize;
40507     },
40508     
40509     /**
40510      * Gets the maximum size for the resizing element
40511      * @return {Number} The maximum size
40512      */
40513     getMaximumSize : function(){
40514         return this.maxSize;
40515     },
40516     
40517     /**
40518      * Sets the maximum size for the resizing element
40519      * @param {Number} maxSize The maximum size
40520      */
40521     setMaximumSize : function(maxSize){
40522         this.maxSize = maxSize;
40523     },
40524     
40525     /**
40526      * Sets the initialize size for the resizing element
40527      * @param {Number} size The initial size
40528      */
40529     setCurrentSize : function(size){
40530         var oldAnimate = this.animate;
40531         this.animate = false;
40532         this.adapter.setElementSize(this, size);
40533         this.animate = oldAnimate;
40534     },
40535     
40536     /**
40537      * Destroy this splitbar. 
40538      * @param {Boolean} removeEl True to remove the element
40539      */
40540     destroy : function(removeEl){
40541         if(this.shim){
40542             this.shim.remove();
40543         }
40544         this.dd.unreg();
40545         this.proxy.parentNode.removeChild(this.proxy);
40546         if(removeEl){
40547             this.el.remove();
40548         }
40549     }
40550 });
40551
40552 /**
40553  * @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.
40554  */
40555 Roo.bootstrap.SplitBar.createProxy = function(dir){
40556     var proxy = new Roo.Element(document.createElement("div"));
40557     proxy.unselectable();
40558     var cls = 'roo-splitbar-proxy';
40559     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40560     document.body.appendChild(proxy.dom);
40561     return proxy.dom;
40562 };
40563
40564 /** 
40565  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40566  * Default Adapter. It assumes the splitter and resizing element are not positioned
40567  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40568  */
40569 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40570 };
40571
40572 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40573     // do nothing for now
40574     init : function(s){
40575     
40576     },
40577     /**
40578      * Called before drag operations to get the current size of the resizing element. 
40579      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40580      */
40581      getElementSize : function(s){
40582         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40583             return s.resizingEl.getWidth();
40584         }else{
40585             return s.resizingEl.getHeight();
40586         }
40587     },
40588     
40589     /**
40590      * Called after drag operations to set the size of the resizing element.
40591      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40592      * @param {Number} newSize The new size to set
40593      * @param {Function} onComplete A function to be invoked when resizing is complete
40594      */
40595     setElementSize : function(s, newSize, onComplete){
40596         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40597             if(!s.animate){
40598                 s.resizingEl.setWidth(newSize);
40599                 if(onComplete){
40600                     onComplete(s, newSize);
40601                 }
40602             }else{
40603                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40604             }
40605         }else{
40606             
40607             if(!s.animate){
40608                 s.resizingEl.setHeight(newSize);
40609                 if(onComplete){
40610                     onComplete(s, newSize);
40611                 }
40612             }else{
40613                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40614             }
40615         }
40616     }
40617 };
40618
40619 /** 
40620  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40621  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40622  * Adapter that  moves the splitter element to align with the resized sizing element. 
40623  * Used with an absolute positioned SplitBar.
40624  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40625  * document.body, make sure you assign an id to the body element.
40626  */
40627 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40628     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40629     this.container = Roo.get(container);
40630 };
40631
40632 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40633     init : function(s){
40634         this.basic.init(s);
40635     },
40636     
40637     getElementSize : function(s){
40638         return this.basic.getElementSize(s);
40639     },
40640     
40641     setElementSize : function(s, newSize, onComplete){
40642         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40643     },
40644     
40645     moveSplitter : function(s){
40646         var yes = Roo.bootstrap.SplitBar;
40647         switch(s.placement){
40648             case yes.LEFT:
40649                 s.el.setX(s.resizingEl.getRight());
40650                 break;
40651             case yes.RIGHT:
40652                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40653                 break;
40654             case yes.TOP:
40655                 s.el.setY(s.resizingEl.getBottom());
40656                 break;
40657             case yes.BOTTOM:
40658                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40659                 break;
40660         }
40661     }
40662 };
40663
40664 /**
40665  * Orientation constant - Create a vertical SplitBar
40666  * @static
40667  * @type Number
40668  */
40669 Roo.bootstrap.SplitBar.VERTICAL = 1;
40670
40671 /**
40672  * Orientation constant - Create a horizontal SplitBar
40673  * @static
40674  * @type Number
40675  */
40676 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40677
40678 /**
40679  * Placement constant - The resizing element is to the left of the splitter element
40680  * @static
40681  * @type Number
40682  */
40683 Roo.bootstrap.SplitBar.LEFT = 1;
40684
40685 /**
40686  * Placement constant - The resizing element is to the right of the splitter element
40687  * @static
40688  * @type Number
40689  */
40690 Roo.bootstrap.SplitBar.RIGHT = 2;
40691
40692 /**
40693  * Placement constant - The resizing element is positioned above the splitter element
40694  * @static
40695  * @type Number
40696  */
40697 Roo.bootstrap.SplitBar.TOP = 3;
40698
40699 /**
40700  * Placement constant - The resizing element is positioned under splitter element
40701  * @static
40702  * @type Number
40703  */
40704 Roo.bootstrap.SplitBar.BOTTOM = 4;
40705 /*
40706  * Based on:
40707  * Ext JS Library 1.1.1
40708  * Copyright(c) 2006-2007, Ext JS, LLC.
40709  *
40710  * Originally Released Under LGPL - original licence link has changed is not relivant.
40711  *
40712  * Fork - LGPL
40713  * <script type="text/javascript">
40714  */
40715
40716 /**
40717  * @class Roo.bootstrap.layout.Manager
40718  * @extends Roo.bootstrap.Component
40719  * @abstract
40720  * Base class for layout managers.
40721  */
40722 Roo.bootstrap.layout.Manager = function(config)
40723 {
40724     this.monitorWindowResize = true; // do this before we apply configuration.
40725     
40726     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40727
40728
40729
40730
40731
40732     /** false to disable window resize monitoring @type Boolean */
40733     
40734     this.regions = {};
40735     this.addEvents({
40736         /**
40737          * @event layout
40738          * Fires when a layout is performed.
40739          * @param {Roo.LayoutManager} this
40740          */
40741         "layout" : true,
40742         /**
40743          * @event regionresized
40744          * Fires when the user resizes a region.
40745          * @param {Roo.LayoutRegion} region The resized region
40746          * @param {Number} newSize The new size (width for east/west, height for north/south)
40747          */
40748         "regionresized" : true,
40749         /**
40750          * @event regioncollapsed
40751          * Fires when a region is collapsed.
40752          * @param {Roo.LayoutRegion} region The collapsed region
40753          */
40754         "regioncollapsed" : true,
40755         /**
40756          * @event regionexpanded
40757          * Fires when a region is expanded.
40758          * @param {Roo.LayoutRegion} region The expanded region
40759          */
40760         "regionexpanded" : true
40761     });
40762     this.updating = false;
40763
40764     if (config.el) {
40765         this.el = Roo.get(config.el);
40766         this.initEvents();
40767     }
40768
40769 };
40770
40771 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40772
40773
40774     regions : null,
40775
40776     monitorWindowResize : true,
40777
40778
40779     updating : false,
40780
40781
40782     onRender : function(ct, position)
40783     {
40784         if(!this.el){
40785             this.el = Roo.get(ct);
40786             this.initEvents();
40787         }
40788         //this.fireEvent('render',this);
40789     },
40790
40791
40792     initEvents: function()
40793     {
40794
40795
40796         // ie scrollbar fix
40797         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40798             document.body.scroll = "no";
40799         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40800             this.el.position('relative');
40801         }
40802         this.id = this.el.id;
40803         this.el.addClass("roo-layout-container");
40804         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40805         if(this.el.dom != document.body ) {
40806             this.el.on('resize', this.layout,this);
40807             this.el.on('show', this.layout,this);
40808         }
40809
40810     },
40811
40812     /**
40813      * Returns true if this layout is currently being updated
40814      * @return {Boolean}
40815      */
40816     isUpdating : function(){
40817         return this.updating;
40818     },
40819
40820     /**
40821      * Suspend the LayoutManager from doing auto-layouts while
40822      * making multiple add or remove calls
40823      */
40824     beginUpdate : function(){
40825         this.updating = true;
40826     },
40827
40828     /**
40829      * Restore auto-layouts and optionally disable the manager from performing a layout
40830      * @param {Boolean} noLayout true to disable a layout update
40831      */
40832     endUpdate : function(noLayout){
40833         this.updating = false;
40834         if(!noLayout){
40835             this.layout();
40836         }
40837     },
40838
40839     layout: function(){
40840         // abstract...
40841     },
40842
40843     onRegionResized : function(region, newSize){
40844         this.fireEvent("regionresized", region, newSize);
40845         this.layout();
40846     },
40847
40848     onRegionCollapsed : function(region){
40849         this.fireEvent("regioncollapsed", region);
40850     },
40851
40852     onRegionExpanded : function(region){
40853         this.fireEvent("regionexpanded", region);
40854     },
40855
40856     /**
40857      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40858      * performs box-model adjustments.
40859      * @return {Object} The size as an object {width: (the width), height: (the height)}
40860      */
40861     getViewSize : function()
40862     {
40863         var size;
40864         if(this.el.dom != document.body){
40865             size = this.el.getSize();
40866         }else{
40867             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40868         }
40869         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40870         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40871         return size;
40872     },
40873
40874     /**
40875      * Returns the Element this layout is bound to.
40876      * @return {Roo.Element}
40877      */
40878     getEl : function(){
40879         return this.el;
40880     },
40881
40882     /**
40883      * Returns the specified region.
40884      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40885      * @return {Roo.LayoutRegion}
40886      */
40887     getRegion : function(target){
40888         return this.regions[target.toLowerCase()];
40889     },
40890
40891     onWindowResize : function(){
40892         if(this.monitorWindowResize){
40893             this.layout();
40894         }
40895     }
40896 });
40897 /*
40898  * Based on:
40899  * Ext JS Library 1.1.1
40900  * Copyright(c) 2006-2007, Ext JS, LLC.
40901  *
40902  * Originally Released Under LGPL - original licence link has changed is not relivant.
40903  *
40904  * Fork - LGPL
40905  * <script type="text/javascript">
40906  */
40907 /**
40908  * @class Roo.bootstrap.layout.Border
40909  * @extends Roo.bootstrap.layout.Manager
40910  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40911  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40912  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40913  * please see: examples/bootstrap/nested.html<br><br>
40914  
40915 <b>The container the layout is rendered into can be either the body element or any other element.
40916 If it is not the body element, the container needs to either be an absolute positioned element,
40917 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40918 the container size if it is not the body element.</b>
40919
40920 * @constructor
40921 * Create a new Border
40922 * @param {Object} config Configuration options
40923  */
40924 Roo.bootstrap.layout.Border = function(config){
40925     config = config || {};
40926     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40927     
40928     
40929     
40930     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40931         if(config[region]){
40932             config[region].region = region;
40933             this.addRegion(config[region]);
40934         }
40935     },this);
40936     
40937 };
40938
40939 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40940
40941 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40942     
40943         /**
40944          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40945          */
40946         /**
40947          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40948          */
40949         /**
40950          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40951          */
40952         /**
40953          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40954          */
40955         /**
40956          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40957          */
40958         
40959         
40960         
40961         
40962     parent : false, // this might point to a 'nest' or a ???
40963     
40964     /**
40965      * Creates and adds a new region if it doesn't already exist.
40966      * @param {String} target The target region key (north, south, east, west or center).
40967      * @param {Object} config The regions config object
40968      * @return {BorderLayoutRegion} The new region
40969      */
40970     addRegion : function(config)
40971     {
40972         if(!this.regions[config.region]){
40973             var r = this.factory(config);
40974             this.bindRegion(r);
40975         }
40976         return this.regions[config.region];
40977     },
40978
40979     // private (kinda)
40980     bindRegion : function(r){
40981         this.regions[r.config.region] = r;
40982         
40983         r.on("visibilitychange",    this.layout, this);
40984         r.on("paneladded",          this.layout, this);
40985         r.on("panelremoved",        this.layout, this);
40986         r.on("invalidated",         this.layout, this);
40987         r.on("resized",             this.onRegionResized, this);
40988         r.on("collapsed",           this.onRegionCollapsed, this);
40989         r.on("expanded",            this.onRegionExpanded, this);
40990     },
40991
40992     /**
40993      * Performs a layout update.
40994      */
40995     layout : function()
40996     {
40997         if(this.updating) {
40998             return;
40999         }
41000         
41001         // render all the rebions if they have not been done alreayd?
41002         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41003             if(this.regions[region] && !this.regions[region].bodyEl){
41004                 this.regions[region].onRender(this.el)
41005             }
41006         },this);
41007         
41008         var size = this.getViewSize();
41009         var w = size.width;
41010         var h = size.height;
41011         var centerW = w;
41012         var centerH = h;
41013         var centerY = 0;
41014         var centerX = 0;
41015         //var x = 0, y = 0;
41016
41017         var rs = this.regions;
41018         var north = rs["north"];
41019         var south = rs["south"]; 
41020         var west = rs["west"];
41021         var east = rs["east"];
41022         var center = rs["center"];
41023         //if(this.hideOnLayout){ // not supported anymore
41024             //c.el.setStyle("display", "none");
41025         //}
41026         if(north && north.isVisible()){
41027             var b = north.getBox();
41028             var m = north.getMargins();
41029             b.width = w - (m.left+m.right);
41030             b.x = m.left;
41031             b.y = m.top;
41032             centerY = b.height + b.y + m.bottom;
41033             centerH -= centerY;
41034             north.updateBox(this.safeBox(b));
41035         }
41036         if(south && south.isVisible()){
41037             var b = south.getBox();
41038             var m = south.getMargins();
41039             b.width = w - (m.left+m.right);
41040             b.x = m.left;
41041             var totalHeight = (b.height + m.top + m.bottom);
41042             b.y = h - totalHeight + m.top;
41043             centerH -= totalHeight;
41044             south.updateBox(this.safeBox(b));
41045         }
41046         if(west && west.isVisible()){
41047             var b = west.getBox();
41048             var m = west.getMargins();
41049             b.height = centerH - (m.top+m.bottom);
41050             b.x = m.left;
41051             b.y = centerY + m.top;
41052             var totalWidth = (b.width + m.left + m.right);
41053             centerX += totalWidth;
41054             centerW -= totalWidth;
41055             west.updateBox(this.safeBox(b));
41056         }
41057         if(east && east.isVisible()){
41058             var b = east.getBox();
41059             var m = east.getMargins();
41060             b.height = centerH - (m.top+m.bottom);
41061             var totalWidth = (b.width + m.left + m.right);
41062             b.x = w - totalWidth + m.left;
41063             b.y = centerY + m.top;
41064             centerW -= totalWidth;
41065             east.updateBox(this.safeBox(b));
41066         }
41067         if(center){
41068             var m = center.getMargins();
41069             var centerBox = {
41070                 x: centerX + m.left,
41071                 y: centerY + m.top,
41072                 width: centerW - (m.left+m.right),
41073                 height: centerH - (m.top+m.bottom)
41074             };
41075             //if(this.hideOnLayout){
41076                 //center.el.setStyle("display", "block");
41077             //}
41078             center.updateBox(this.safeBox(centerBox));
41079         }
41080         this.el.repaint();
41081         this.fireEvent("layout", this);
41082     },
41083
41084     // private
41085     safeBox : function(box){
41086         box.width = Math.max(0, box.width);
41087         box.height = Math.max(0, box.height);
41088         return box;
41089     },
41090
41091     /**
41092      * Adds a ContentPanel (or subclass) to this layout.
41093      * @param {String} target The target region key (north, south, east, west or center).
41094      * @param {Roo.ContentPanel} panel The panel to add
41095      * @return {Roo.ContentPanel} The added panel
41096      */
41097     add : function(target, panel){
41098          
41099         target = target.toLowerCase();
41100         return this.regions[target].add(panel);
41101     },
41102
41103     /**
41104      * Remove a ContentPanel (or subclass) to this layout.
41105      * @param {String} target The target region key (north, south, east, west or center).
41106      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41107      * @return {Roo.ContentPanel} The removed panel
41108      */
41109     remove : function(target, panel){
41110         target = target.toLowerCase();
41111         return this.regions[target].remove(panel);
41112     },
41113
41114     /**
41115      * Searches all regions for a panel with the specified id
41116      * @param {String} panelId
41117      * @return {Roo.ContentPanel} The panel or null if it wasn't found
41118      */
41119     findPanel : function(panelId){
41120         var rs = this.regions;
41121         for(var target in rs){
41122             if(typeof rs[target] != "function"){
41123                 var p = rs[target].getPanel(panelId);
41124                 if(p){
41125                     return p;
41126                 }
41127             }
41128         }
41129         return null;
41130     },
41131
41132     /**
41133      * Searches all regions for a panel with the specified id and activates (shows) it.
41134      * @param {String/ContentPanel} panelId The panels id or the panel itself
41135      * @return {Roo.ContentPanel} The shown panel or null
41136      */
41137     showPanel : function(panelId) {
41138       var rs = this.regions;
41139       for(var target in rs){
41140          var r = rs[target];
41141          if(typeof r != "function"){
41142             if(r.hasPanel(panelId)){
41143                return r.showPanel(panelId);
41144             }
41145          }
41146       }
41147       return null;
41148    },
41149
41150    /**
41151      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41152      * @param {Roo.state.Provider} provider (optional) An alternate state provider
41153      */
41154    /*
41155     restoreState : function(provider){
41156         if(!provider){
41157             provider = Roo.state.Manager;
41158         }
41159         var sm = new Roo.LayoutStateManager();
41160         sm.init(this, provider);
41161     },
41162 */
41163  
41164  
41165     /**
41166      * Adds a xtype elements to the layout.
41167      * <pre><code>
41168
41169 layout.addxtype({
41170        xtype : 'ContentPanel',
41171        region: 'west',
41172        items: [ .... ]
41173    }
41174 );
41175
41176 layout.addxtype({
41177         xtype : 'NestedLayoutPanel',
41178         region: 'west',
41179         layout: {
41180            center: { },
41181            west: { }   
41182         },
41183         items : [ ... list of content panels or nested layout panels.. ]
41184    }
41185 );
41186 </code></pre>
41187      * @param {Object} cfg Xtype definition of item to add.
41188      */
41189     addxtype : function(cfg)
41190     {
41191         // basically accepts a pannel...
41192         // can accept a layout region..!?!?
41193         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41194         
41195         
41196         // theory?  children can only be panels??
41197         
41198         //if (!cfg.xtype.match(/Panel$/)) {
41199         //    return false;
41200         //}
41201         var ret = false;
41202         
41203         if (typeof(cfg.region) == 'undefined') {
41204             Roo.log("Failed to add Panel, region was not set");
41205             Roo.log(cfg);
41206             return false;
41207         }
41208         var region = cfg.region;
41209         delete cfg.region;
41210         
41211           
41212         var xitems = [];
41213         if (cfg.items) {
41214             xitems = cfg.items;
41215             delete cfg.items;
41216         }
41217         var nb = false;
41218         
41219         if ( region == 'center') {
41220             Roo.log("Center: " + cfg.title);
41221         }
41222         
41223         
41224         switch(cfg.xtype) 
41225         {
41226             case 'Content':  // ContentPanel (el, cfg)
41227             case 'Scroll':  // ContentPanel (el, cfg)
41228             case 'View': 
41229                 cfg.autoCreate = cfg.autoCreate || true;
41230                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41231                 //} else {
41232                 //    var el = this.el.createChild();
41233                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41234                 //}
41235                 
41236                 this.add(region, ret);
41237                 break;
41238             
41239             /*
41240             case 'TreePanel': // our new panel!
41241                 cfg.el = this.el.createChild();
41242                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41243                 this.add(region, ret);
41244                 break;
41245             */
41246             
41247             case 'Nest': 
41248                 // create a new Layout (which is  a Border Layout...
41249                 
41250                 var clayout = cfg.layout;
41251                 clayout.el  = this.el.createChild();
41252                 clayout.items   = clayout.items  || [];
41253                 
41254                 delete cfg.layout;
41255                 
41256                 // replace this exitems with the clayout ones..
41257                 xitems = clayout.items;
41258                  
41259                 // force background off if it's in center...
41260                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41261                     cfg.background = false;
41262                 }
41263                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41264                 
41265                 
41266                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41267                 //console.log('adding nested layout panel '  + cfg.toSource());
41268                 this.add(region, ret);
41269                 nb = {}; /// find first...
41270                 break;
41271             
41272             case 'Grid':
41273                 
41274                 // needs grid and region
41275                 
41276                 //var el = this.getRegion(region).el.createChild();
41277                 /*
41278                  *var el = this.el.createChild();
41279                 // create the grid first...
41280                 cfg.grid.container = el;
41281                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41282                 */
41283                 
41284                 if (region == 'center' && this.active ) {
41285                     cfg.background = false;
41286                 }
41287                 
41288                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41289                 
41290                 this.add(region, ret);
41291                 /*
41292                 if (cfg.background) {
41293                     // render grid on panel activation (if panel background)
41294                     ret.on('activate', function(gp) {
41295                         if (!gp.grid.rendered) {
41296                     //        gp.grid.render(el);
41297                         }
41298                     });
41299                 } else {
41300                   //  cfg.grid.render(el);
41301                 }
41302                 */
41303                 break;
41304            
41305            
41306             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41307                 // it was the old xcomponent building that caused this before.
41308                 // espeically if border is the top element in the tree.
41309                 ret = this;
41310                 break; 
41311                 
41312                     
41313                 
41314                 
41315                 
41316             default:
41317                 /*
41318                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41319                     
41320                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41321                     this.add(region, ret);
41322                 } else {
41323                 */
41324                     Roo.log(cfg);
41325                     throw "Can not add '" + cfg.xtype + "' to Border";
41326                     return null;
41327              
41328                                 
41329              
41330         }
41331         this.beginUpdate();
41332         // add children..
41333         var region = '';
41334         var abn = {};
41335         Roo.each(xitems, function(i)  {
41336             region = nb && i.region ? i.region : false;
41337             
41338             var add = ret.addxtype(i);
41339            
41340             if (region) {
41341                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41342                 if (!i.background) {
41343                     abn[region] = nb[region] ;
41344                 }
41345             }
41346             
41347         });
41348         this.endUpdate();
41349
41350         // make the last non-background panel active..
41351         //if (nb) { Roo.log(abn); }
41352         if (nb) {
41353             
41354             for(var r in abn) {
41355                 region = this.getRegion(r);
41356                 if (region) {
41357                     // tried using nb[r], but it does not work..
41358                      
41359                     region.showPanel(abn[r]);
41360                    
41361                 }
41362             }
41363         }
41364         return ret;
41365         
41366     },
41367     
41368     
41369 // private
41370     factory : function(cfg)
41371     {
41372         
41373         var validRegions = Roo.bootstrap.layout.Border.regions;
41374
41375         var target = cfg.region;
41376         cfg.mgr = this;
41377         
41378         var r = Roo.bootstrap.layout;
41379         Roo.log(target);
41380         switch(target){
41381             case "north":
41382                 return new r.North(cfg);
41383             case "south":
41384                 return new r.South(cfg);
41385             case "east":
41386                 return new r.East(cfg);
41387             case "west":
41388                 return new r.West(cfg);
41389             case "center":
41390                 return new r.Center(cfg);
41391         }
41392         throw 'Layout region "'+target+'" not supported.';
41393     }
41394     
41395     
41396 });
41397  /*
41398  * Based on:
41399  * Ext JS Library 1.1.1
41400  * Copyright(c) 2006-2007, Ext JS, LLC.
41401  *
41402  * Originally Released Under LGPL - original licence link has changed is not relivant.
41403  *
41404  * Fork - LGPL
41405  * <script type="text/javascript">
41406  */
41407  
41408 /**
41409  * @class Roo.bootstrap.layout.Basic
41410  * @extends Roo.util.Observable
41411  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41412  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41413  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41414  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41415  * @cfg {string}   region  the region that it inhabits..
41416  * @cfg {bool}   skipConfig skip config?
41417  * 
41418
41419  */
41420 Roo.bootstrap.layout.Basic = function(config){
41421     
41422     this.mgr = config.mgr;
41423     
41424     this.position = config.region;
41425     
41426     var skipConfig = config.skipConfig;
41427     
41428     this.events = {
41429         /**
41430          * @scope Roo.BasicLayoutRegion
41431          */
41432         
41433         /**
41434          * @event beforeremove
41435          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41436          * @param {Roo.LayoutRegion} this
41437          * @param {Roo.ContentPanel} panel The panel
41438          * @param {Object} e The cancel event object
41439          */
41440         "beforeremove" : true,
41441         /**
41442          * @event invalidated
41443          * Fires when the layout for this region is changed.
41444          * @param {Roo.LayoutRegion} this
41445          */
41446         "invalidated" : true,
41447         /**
41448          * @event visibilitychange
41449          * Fires when this region is shown or hidden 
41450          * @param {Roo.LayoutRegion} this
41451          * @param {Boolean} visibility true or false
41452          */
41453         "visibilitychange" : true,
41454         /**
41455          * @event paneladded
41456          * Fires when a panel is added. 
41457          * @param {Roo.LayoutRegion} this
41458          * @param {Roo.ContentPanel} panel The panel
41459          */
41460         "paneladded" : true,
41461         /**
41462          * @event panelremoved
41463          * Fires when a panel is removed. 
41464          * @param {Roo.LayoutRegion} this
41465          * @param {Roo.ContentPanel} panel The panel
41466          */
41467         "panelremoved" : true,
41468         /**
41469          * @event beforecollapse
41470          * Fires when this region before collapse.
41471          * @param {Roo.LayoutRegion} this
41472          */
41473         "beforecollapse" : true,
41474         /**
41475          * @event collapsed
41476          * Fires when this region is collapsed.
41477          * @param {Roo.LayoutRegion} this
41478          */
41479         "collapsed" : true,
41480         /**
41481          * @event expanded
41482          * Fires when this region is expanded.
41483          * @param {Roo.LayoutRegion} this
41484          */
41485         "expanded" : true,
41486         /**
41487          * @event slideshow
41488          * Fires when this region is slid into view.
41489          * @param {Roo.LayoutRegion} this
41490          */
41491         "slideshow" : true,
41492         /**
41493          * @event slidehide
41494          * Fires when this region slides out of view. 
41495          * @param {Roo.LayoutRegion} this
41496          */
41497         "slidehide" : true,
41498         /**
41499          * @event panelactivated
41500          * Fires when a panel is activated. 
41501          * @param {Roo.LayoutRegion} this
41502          * @param {Roo.ContentPanel} panel The activated panel
41503          */
41504         "panelactivated" : true,
41505         /**
41506          * @event resized
41507          * Fires when the user resizes this region. 
41508          * @param {Roo.LayoutRegion} this
41509          * @param {Number} newSize The new size (width for east/west, height for north/south)
41510          */
41511         "resized" : true
41512     };
41513     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41514     this.panels = new Roo.util.MixedCollection();
41515     this.panels.getKey = this.getPanelId.createDelegate(this);
41516     this.box = null;
41517     this.activePanel = null;
41518     // ensure listeners are added...
41519     
41520     if (config.listeners || config.events) {
41521         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41522             listeners : config.listeners || {},
41523             events : config.events || {}
41524         });
41525     }
41526     
41527     if(skipConfig !== true){
41528         this.applyConfig(config);
41529     }
41530 };
41531
41532 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41533 {
41534     getPanelId : function(p){
41535         return p.getId();
41536     },
41537     
41538     applyConfig : function(config){
41539         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41540         this.config = config;
41541         
41542     },
41543     
41544     /**
41545      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41546      * the width, for horizontal (north, south) the height.
41547      * @param {Number} newSize The new width or height
41548      */
41549     resizeTo : function(newSize){
41550         var el = this.el ? this.el :
41551                  (this.activePanel ? this.activePanel.getEl() : null);
41552         if(el){
41553             switch(this.position){
41554                 case "east":
41555                 case "west":
41556                     el.setWidth(newSize);
41557                     this.fireEvent("resized", this, newSize);
41558                 break;
41559                 case "north":
41560                 case "south":
41561                     el.setHeight(newSize);
41562                     this.fireEvent("resized", this, newSize);
41563                 break;                
41564             }
41565         }
41566     },
41567     
41568     getBox : function(){
41569         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41570     },
41571     
41572     getMargins : function(){
41573         return this.margins;
41574     },
41575     
41576     updateBox : function(box){
41577         this.box = box;
41578         var el = this.activePanel.getEl();
41579         el.dom.style.left = box.x + "px";
41580         el.dom.style.top = box.y + "px";
41581         this.activePanel.setSize(box.width, box.height);
41582     },
41583     
41584     /**
41585      * Returns the container element for this region.
41586      * @return {Roo.Element}
41587      */
41588     getEl : function(){
41589         return this.activePanel;
41590     },
41591     
41592     /**
41593      * Returns true if this region is currently visible.
41594      * @return {Boolean}
41595      */
41596     isVisible : function(){
41597         return this.activePanel ? true : false;
41598     },
41599     
41600     setActivePanel : function(panel){
41601         panel = this.getPanel(panel);
41602         if(this.activePanel && this.activePanel != panel){
41603             this.activePanel.setActiveState(false);
41604             this.activePanel.getEl().setLeftTop(-10000,-10000);
41605         }
41606         this.activePanel = panel;
41607         panel.setActiveState(true);
41608         if(this.box){
41609             panel.setSize(this.box.width, this.box.height);
41610         }
41611         this.fireEvent("panelactivated", this, panel);
41612         this.fireEvent("invalidated");
41613     },
41614     
41615     /**
41616      * Show the specified panel.
41617      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41618      * @return {Roo.ContentPanel} The shown panel or null
41619      */
41620     showPanel : function(panel){
41621         panel = this.getPanel(panel);
41622         if(panel){
41623             this.setActivePanel(panel);
41624         }
41625         return panel;
41626     },
41627     
41628     /**
41629      * Get the active panel for this region.
41630      * @return {Roo.ContentPanel} The active panel or null
41631      */
41632     getActivePanel : function(){
41633         return this.activePanel;
41634     },
41635     
41636     /**
41637      * Add the passed ContentPanel(s)
41638      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41639      * @return {Roo.ContentPanel} The panel added (if only one was added)
41640      */
41641     add : function(panel){
41642         if(arguments.length > 1){
41643             for(var i = 0, len = arguments.length; i < len; i++) {
41644                 this.add(arguments[i]);
41645             }
41646             return null;
41647         }
41648         if(this.hasPanel(panel)){
41649             this.showPanel(panel);
41650             return panel;
41651         }
41652         var el = panel.getEl();
41653         if(el.dom.parentNode != this.mgr.el.dom){
41654             this.mgr.el.dom.appendChild(el.dom);
41655         }
41656         if(panel.setRegion){
41657             panel.setRegion(this);
41658         }
41659         this.panels.add(panel);
41660         el.setStyle("position", "absolute");
41661         if(!panel.background){
41662             this.setActivePanel(panel);
41663             if(this.config.initialSize && this.panels.getCount()==1){
41664                 this.resizeTo(this.config.initialSize);
41665             }
41666         }
41667         this.fireEvent("paneladded", this, panel);
41668         return panel;
41669     },
41670     
41671     /**
41672      * Returns true if the panel is in this region.
41673      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41674      * @return {Boolean}
41675      */
41676     hasPanel : function(panel){
41677         if(typeof panel == "object"){ // must be panel obj
41678             panel = panel.getId();
41679         }
41680         return this.getPanel(panel) ? true : false;
41681     },
41682     
41683     /**
41684      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41685      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41686      * @param {Boolean} preservePanel Overrides the config preservePanel option
41687      * @return {Roo.ContentPanel} The panel that was removed
41688      */
41689     remove : function(panel, preservePanel){
41690         panel = this.getPanel(panel);
41691         if(!panel){
41692             return null;
41693         }
41694         var e = {};
41695         this.fireEvent("beforeremove", this, panel, e);
41696         if(e.cancel === true){
41697             return null;
41698         }
41699         var panelId = panel.getId();
41700         this.panels.removeKey(panelId);
41701         return panel;
41702     },
41703     
41704     /**
41705      * Returns the panel specified or null if it's not in this region.
41706      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41707      * @return {Roo.ContentPanel}
41708      */
41709     getPanel : function(id){
41710         if(typeof id == "object"){ // must be panel obj
41711             return id;
41712         }
41713         return this.panels.get(id);
41714     },
41715     
41716     /**
41717      * Returns this regions position (north/south/east/west/center).
41718      * @return {String} 
41719      */
41720     getPosition: function(){
41721         return this.position;    
41722     }
41723 });/*
41724  * Based on:
41725  * Ext JS Library 1.1.1
41726  * Copyright(c) 2006-2007, Ext JS, LLC.
41727  *
41728  * Originally Released Under LGPL - original licence link has changed is not relivant.
41729  *
41730  * Fork - LGPL
41731  * <script type="text/javascript">
41732  */
41733  
41734 /**
41735  * @class Roo.bootstrap.layout.Region
41736  * @extends Roo.bootstrap.layout.Basic
41737  * This class represents a region in a layout manager.
41738  
41739  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41740  * @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})
41741  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41742  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41743  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41744  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41745  * @cfg {String}    title           The title for the region (overrides panel titles)
41746  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41747  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41748  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41749  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41750  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41751  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41752  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41753  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41754  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41755  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41756
41757  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41758  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41759  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41760  * @cfg {Number}    width           For East/West panels
41761  * @cfg {Number}    height          For North/South panels
41762  * @cfg {Boolean}   split           To show the splitter
41763  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41764  * 
41765  * @cfg {string}   cls             Extra CSS classes to add to region
41766  * 
41767  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41768  * @cfg {string}   region  the region that it inhabits..
41769  *
41770
41771  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41772  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41773
41774  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41775  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41776  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41777  */
41778 Roo.bootstrap.layout.Region = function(config)
41779 {
41780     this.applyConfig(config);
41781
41782     var mgr = config.mgr;
41783     var pos = config.region;
41784     config.skipConfig = true;
41785     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41786     
41787     if (mgr.el) {
41788         this.onRender(mgr.el);   
41789     }
41790      
41791     this.visible = true;
41792     this.collapsed = false;
41793     this.unrendered_panels = [];
41794 };
41795
41796 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41797
41798     position: '', // set by wrapper (eg. north/south etc..)
41799     unrendered_panels : null,  // unrendered panels.
41800     
41801     tabPosition : false,
41802     
41803     mgr: false, // points to 'Border'
41804     
41805     
41806     createBody : function(){
41807         /** This region's body element 
41808         * @type Roo.Element */
41809         this.bodyEl = this.el.createChild({
41810                 tag: "div",
41811                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41812         });
41813     },
41814
41815     onRender: function(ctr, pos)
41816     {
41817         var dh = Roo.DomHelper;
41818         /** This region's container element 
41819         * @type Roo.Element */
41820         this.el = dh.append(ctr.dom, {
41821                 tag: "div",
41822                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41823             }, true);
41824         /** This region's title element 
41825         * @type Roo.Element */
41826     
41827         this.titleEl = dh.append(this.el.dom,  {
41828                 tag: "div",
41829                 unselectable: "on",
41830                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41831                 children:[
41832                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41833                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41834                 ]
41835             }, true);
41836         
41837         this.titleEl.enableDisplayMode();
41838         /** This region's title text element 
41839         * @type HTMLElement */
41840         this.titleTextEl = this.titleEl.dom.firstChild;
41841         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41842         /*
41843         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41844         this.closeBtn.enableDisplayMode();
41845         this.closeBtn.on("click", this.closeClicked, this);
41846         this.closeBtn.hide();
41847     */
41848         this.createBody(this.config);
41849         if(this.config.hideWhenEmpty){
41850             this.hide();
41851             this.on("paneladded", this.validateVisibility, this);
41852             this.on("panelremoved", this.validateVisibility, this);
41853         }
41854         if(this.autoScroll){
41855             this.bodyEl.setStyle("overflow", "auto");
41856         }else{
41857             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41858         }
41859         //if(c.titlebar !== false){
41860             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41861                 this.titleEl.hide();
41862             }else{
41863                 this.titleEl.show();
41864                 if(this.config.title){
41865                     this.titleTextEl.innerHTML = this.config.title;
41866                 }
41867             }
41868         //}
41869         if(this.config.collapsed){
41870             this.collapse(true);
41871         }
41872         if(this.config.hidden){
41873             this.hide();
41874         }
41875         
41876         if (this.unrendered_panels && this.unrendered_panels.length) {
41877             for (var i =0;i< this.unrendered_panels.length; i++) {
41878                 this.add(this.unrendered_panels[i]);
41879             }
41880             this.unrendered_panels = null;
41881             
41882         }
41883         
41884     },
41885     
41886     applyConfig : function(c)
41887     {
41888         /*
41889          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41890             var dh = Roo.DomHelper;
41891             if(c.titlebar !== false){
41892                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41893                 this.collapseBtn.on("click", this.collapse, this);
41894                 this.collapseBtn.enableDisplayMode();
41895                 /*
41896                 if(c.showPin === true || this.showPin){
41897                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41898                     this.stickBtn.enableDisplayMode();
41899                     this.stickBtn.on("click", this.expand, this);
41900                     this.stickBtn.hide();
41901                 }
41902                 
41903             }
41904             */
41905             /** This region's collapsed element
41906             * @type Roo.Element */
41907             /*
41908              *
41909             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41910                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41911             ]}, true);
41912             
41913             if(c.floatable !== false){
41914                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41915                this.collapsedEl.on("click", this.collapseClick, this);
41916             }
41917
41918             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41919                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41920                    id: "message", unselectable: "on", style:{"float":"left"}});
41921                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41922              }
41923             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41924             this.expandBtn.on("click", this.expand, this);
41925             
41926         }
41927         
41928         if(this.collapseBtn){
41929             this.collapseBtn.setVisible(c.collapsible == true);
41930         }
41931         
41932         this.cmargins = c.cmargins || this.cmargins ||
41933                          (this.position == "west" || this.position == "east" ?
41934                              {top: 0, left: 2, right:2, bottom: 0} :
41935                              {top: 2, left: 0, right:0, bottom: 2});
41936         */
41937         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41938         
41939         
41940         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41941         
41942         this.autoScroll = c.autoScroll || false;
41943         
41944         
41945        
41946         
41947         this.duration = c.duration || .30;
41948         this.slideDuration = c.slideDuration || .45;
41949         this.config = c;
41950        
41951     },
41952     /**
41953      * Returns true if this region is currently visible.
41954      * @return {Boolean}
41955      */
41956     isVisible : function(){
41957         return this.visible;
41958     },
41959
41960     /**
41961      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41962      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41963      */
41964     //setCollapsedTitle : function(title){
41965     //    title = title || "&#160;";
41966      //   if(this.collapsedTitleTextEl){
41967       //      this.collapsedTitleTextEl.innerHTML = title;
41968        // }
41969     //},
41970
41971     getBox : function(){
41972         var b;
41973       //  if(!this.collapsed){
41974             b = this.el.getBox(false, true);
41975        // }else{
41976           //  b = this.collapsedEl.getBox(false, true);
41977         //}
41978         return b;
41979     },
41980
41981     getMargins : function(){
41982         return this.margins;
41983         //return this.collapsed ? this.cmargins : this.margins;
41984     },
41985 /*
41986     highlight : function(){
41987         this.el.addClass("x-layout-panel-dragover");
41988     },
41989
41990     unhighlight : function(){
41991         this.el.removeClass("x-layout-panel-dragover");
41992     },
41993 */
41994     updateBox : function(box)
41995     {
41996         if (!this.bodyEl) {
41997             return; // not rendered yet..
41998         }
41999         
42000         this.box = box;
42001         if(!this.collapsed){
42002             this.el.dom.style.left = box.x + "px";
42003             this.el.dom.style.top = box.y + "px";
42004             this.updateBody(box.width, box.height);
42005         }else{
42006             this.collapsedEl.dom.style.left = box.x + "px";
42007             this.collapsedEl.dom.style.top = box.y + "px";
42008             this.collapsedEl.setSize(box.width, box.height);
42009         }
42010         if(this.tabs){
42011             this.tabs.autoSizeTabs();
42012         }
42013     },
42014
42015     updateBody : function(w, h)
42016     {
42017         if(w !== null){
42018             this.el.setWidth(w);
42019             w -= this.el.getBorderWidth("rl");
42020             if(this.config.adjustments){
42021                 w += this.config.adjustments[0];
42022             }
42023         }
42024         if(h !== null && h > 0){
42025             this.el.setHeight(h);
42026             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42027             h -= this.el.getBorderWidth("tb");
42028             if(this.config.adjustments){
42029                 h += this.config.adjustments[1];
42030             }
42031             this.bodyEl.setHeight(h);
42032             if(this.tabs){
42033                 h = this.tabs.syncHeight(h);
42034             }
42035         }
42036         if(this.panelSize){
42037             w = w !== null ? w : this.panelSize.width;
42038             h = h !== null ? h : this.panelSize.height;
42039         }
42040         if(this.activePanel){
42041             var el = this.activePanel.getEl();
42042             w = w !== null ? w : el.getWidth();
42043             h = h !== null ? h : el.getHeight();
42044             this.panelSize = {width: w, height: h};
42045             this.activePanel.setSize(w, h);
42046         }
42047         if(Roo.isIE && this.tabs){
42048             this.tabs.el.repaint();
42049         }
42050     },
42051
42052     /**
42053      * Returns the container element for this region.
42054      * @return {Roo.Element}
42055      */
42056     getEl : function(){
42057         return this.el;
42058     },
42059
42060     /**
42061      * Hides this region.
42062      */
42063     hide : function(){
42064         //if(!this.collapsed){
42065             this.el.dom.style.left = "-2000px";
42066             this.el.hide();
42067         //}else{
42068          //   this.collapsedEl.dom.style.left = "-2000px";
42069          //   this.collapsedEl.hide();
42070        // }
42071         this.visible = false;
42072         this.fireEvent("visibilitychange", this, false);
42073     },
42074
42075     /**
42076      * Shows this region if it was previously hidden.
42077      */
42078     show : function(){
42079         //if(!this.collapsed){
42080             this.el.show();
42081         //}else{
42082         //    this.collapsedEl.show();
42083        // }
42084         this.visible = true;
42085         this.fireEvent("visibilitychange", this, true);
42086     },
42087 /*
42088     closeClicked : function(){
42089         if(this.activePanel){
42090             this.remove(this.activePanel);
42091         }
42092     },
42093
42094     collapseClick : function(e){
42095         if(this.isSlid){
42096            e.stopPropagation();
42097            this.slideIn();
42098         }else{
42099            e.stopPropagation();
42100            this.slideOut();
42101         }
42102     },
42103 */
42104     /**
42105      * Collapses this region.
42106      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42107      */
42108     /*
42109     collapse : function(skipAnim, skipCheck = false){
42110         if(this.collapsed) {
42111             return;
42112         }
42113         
42114         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42115             
42116             this.collapsed = true;
42117             if(this.split){
42118                 this.split.el.hide();
42119             }
42120             if(this.config.animate && skipAnim !== true){
42121                 this.fireEvent("invalidated", this);
42122                 this.animateCollapse();
42123             }else{
42124                 this.el.setLocation(-20000,-20000);
42125                 this.el.hide();
42126                 this.collapsedEl.show();
42127                 this.fireEvent("collapsed", this);
42128                 this.fireEvent("invalidated", this);
42129             }
42130         }
42131         
42132     },
42133 */
42134     animateCollapse : function(){
42135         // overridden
42136     },
42137
42138     /**
42139      * Expands this region if it was previously collapsed.
42140      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42141      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42142      */
42143     /*
42144     expand : function(e, skipAnim){
42145         if(e) {
42146             e.stopPropagation();
42147         }
42148         if(!this.collapsed || this.el.hasActiveFx()) {
42149             return;
42150         }
42151         if(this.isSlid){
42152             this.afterSlideIn();
42153             skipAnim = true;
42154         }
42155         this.collapsed = false;
42156         if(this.config.animate && skipAnim !== true){
42157             this.animateExpand();
42158         }else{
42159             this.el.show();
42160             if(this.split){
42161                 this.split.el.show();
42162             }
42163             this.collapsedEl.setLocation(-2000,-2000);
42164             this.collapsedEl.hide();
42165             this.fireEvent("invalidated", this);
42166             this.fireEvent("expanded", this);
42167         }
42168     },
42169 */
42170     animateExpand : function(){
42171         // overridden
42172     },
42173
42174     initTabs : function()
42175     {
42176         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42177         
42178         var ts = new Roo.bootstrap.panel.Tabs({
42179             el: this.bodyEl.dom,
42180             region : this,
42181             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
42182             disableTooltips: this.config.disableTabTips,
42183             toolbar : this.config.toolbar
42184         });
42185         
42186         if(this.config.hideTabs){
42187             ts.stripWrap.setDisplayed(false);
42188         }
42189         this.tabs = ts;
42190         ts.resizeTabs = this.config.resizeTabs === true;
42191         ts.minTabWidth = this.config.minTabWidth || 40;
42192         ts.maxTabWidth = this.config.maxTabWidth || 250;
42193         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42194         ts.monitorResize = false;
42195         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42196         ts.bodyEl.addClass('roo-layout-tabs-body');
42197         this.panels.each(this.initPanelAsTab, this);
42198     },
42199
42200     initPanelAsTab : function(panel){
42201         var ti = this.tabs.addTab(
42202             panel.getEl().id,
42203             panel.getTitle(),
42204             null,
42205             this.config.closeOnTab && panel.isClosable(),
42206             panel.tpl
42207         );
42208         if(panel.tabTip !== undefined){
42209             ti.setTooltip(panel.tabTip);
42210         }
42211         ti.on("activate", function(){
42212               this.setActivePanel(panel);
42213         }, this);
42214         
42215         if(this.config.closeOnTab){
42216             ti.on("beforeclose", function(t, e){
42217                 e.cancel = true;
42218                 this.remove(panel);
42219             }, this);
42220         }
42221         
42222         panel.tabItem = ti;
42223         
42224         return ti;
42225     },
42226
42227     updatePanelTitle : function(panel, title)
42228     {
42229         if(this.activePanel == panel){
42230             this.updateTitle(title);
42231         }
42232         if(this.tabs){
42233             var ti = this.tabs.getTab(panel.getEl().id);
42234             ti.setText(title);
42235             if(panel.tabTip !== undefined){
42236                 ti.setTooltip(panel.tabTip);
42237             }
42238         }
42239     },
42240
42241     updateTitle : function(title){
42242         if(this.titleTextEl && !this.config.title){
42243             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42244         }
42245     },
42246
42247     setActivePanel : function(panel)
42248     {
42249         panel = this.getPanel(panel);
42250         if(this.activePanel && this.activePanel != panel){
42251             if(this.activePanel.setActiveState(false) === false){
42252                 return;
42253             }
42254         }
42255         this.activePanel = panel;
42256         panel.setActiveState(true);
42257         if(this.panelSize){
42258             panel.setSize(this.panelSize.width, this.panelSize.height);
42259         }
42260         if(this.closeBtn){
42261             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42262         }
42263         this.updateTitle(panel.getTitle());
42264         if(this.tabs){
42265             this.fireEvent("invalidated", this);
42266         }
42267         this.fireEvent("panelactivated", this, panel);
42268     },
42269
42270     /**
42271      * Shows the specified panel.
42272      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42273      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42274      */
42275     showPanel : function(panel)
42276     {
42277         panel = this.getPanel(panel);
42278         if(panel){
42279             if(this.tabs){
42280                 var tab = this.tabs.getTab(panel.getEl().id);
42281                 if(tab.isHidden()){
42282                     this.tabs.unhideTab(tab.id);
42283                 }
42284                 tab.activate();
42285             }else{
42286                 this.setActivePanel(panel);
42287             }
42288         }
42289         return panel;
42290     },
42291
42292     /**
42293      * Get the active panel for this region.
42294      * @return {Roo.ContentPanel} The active panel or null
42295      */
42296     getActivePanel : function(){
42297         return this.activePanel;
42298     },
42299
42300     validateVisibility : function(){
42301         if(this.panels.getCount() < 1){
42302             this.updateTitle("&#160;");
42303             this.closeBtn.hide();
42304             this.hide();
42305         }else{
42306             if(!this.isVisible()){
42307                 this.show();
42308             }
42309         }
42310     },
42311
42312     /**
42313      * Adds the passed ContentPanel(s) to this region.
42314      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42315      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42316      */
42317     add : function(panel)
42318     {
42319         if(arguments.length > 1){
42320             for(var i = 0, len = arguments.length; i < len; i++) {
42321                 this.add(arguments[i]);
42322             }
42323             return null;
42324         }
42325         
42326         // if we have not been rendered yet, then we can not really do much of this..
42327         if (!this.bodyEl) {
42328             this.unrendered_panels.push(panel);
42329             return panel;
42330         }
42331         
42332         
42333         
42334         
42335         if(this.hasPanel(panel)){
42336             this.showPanel(panel);
42337             return panel;
42338         }
42339         panel.setRegion(this);
42340         this.panels.add(panel);
42341        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42342             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42343             // and hide them... ???
42344             this.bodyEl.dom.appendChild(panel.getEl().dom);
42345             if(panel.background !== true){
42346                 this.setActivePanel(panel);
42347             }
42348             this.fireEvent("paneladded", this, panel);
42349             return panel;
42350         }
42351         */
42352         if(!this.tabs){
42353             this.initTabs();
42354         }else{
42355             this.initPanelAsTab(panel);
42356         }
42357         
42358         
42359         if(panel.background !== true){
42360             this.tabs.activate(panel.getEl().id);
42361         }
42362         this.fireEvent("paneladded", this, panel);
42363         return panel;
42364     },
42365
42366     /**
42367      * Hides the tab for the specified panel.
42368      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42369      */
42370     hidePanel : function(panel){
42371         if(this.tabs && (panel = this.getPanel(panel))){
42372             this.tabs.hideTab(panel.getEl().id);
42373         }
42374     },
42375
42376     /**
42377      * Unhides the tab for a previously hidden panel.
42378      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42379      */
42380     unhidePanel : function(panel){
42381         if(this.tabs && (panel = this.getPanel(panel))){
42382             this.tabs.unhideTab(panel.getEl().id);
42383         }
42384     },
42385
42386     clearPanels : function(){
42387         while(this.panels.getCount() > 0){
42388              this.remove(this.panels.first());
42389         }
42390     },
42391
42392     /**
42393      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42394      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42395      * @param {Boolean} preservePanel Overrides the config preservePanel option
42396      * @return {Roo.ContentPanel} The panel that was removed
42397      */
42398     remove : function(panel, preservePanel)
42399     {
42400         panel = this.getPanel(panel);
42401         if(!panel){
42402             return null;
42403         }
42404         var e = {};
42405         this.fireEvent("beforeremove", this, panel, e);
42406         if(e.cancel === true){
42407             return null;
42408         }
42409         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42410         var panelId = panel.getId();
42411         this.panels.removeKey(panelId);
42412         if(preservePanel){
42413             document.body.appendChild(panel.getEl().dom);
42414         }
42415         if(this.tabs){
42416             this.tabs.removeTab(panel.getEl().id);
42417         }else if (!preservePanel){
42418             this.bodyEl.dom.removeChild(panel.getEl().dom);
42419         }
42420         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42421             var p = this.panels.first();
42422             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42423             tempEl.appendChild(p.getEl().dom);
42424             this.bodyEl.update("");
42425             this.bodyEl.dom.appendChild(p.getEl().dom);
42426             tempEl = null;
42427             this.updateTitle(p.getTitle());
42428             this.tabs = null;
42429             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42430             this.setActivePanel(p);
42431         }
42432         panel.setRegion(null);
42433         if(this.activePanel == panel){
42434             this.activePanel = null;
42435         }
42436         if(this.config.autoDestroy !== false && preservePanel !== true){
42437             try{panel.destroy();}catch(e){}
42438         }
42439         this.fireEvent("panelremoved", this, panel);
42440         return panel;
42441     },
42442
42443     /**
42444      * Returns the TabPanel component used by this region
42445      * @return {Roo.TabPanel}
42446      */
42447     getTabs : function(){
42448         return this.tabs;
42449     },
42450
42451     createTool : function(parentEl, className){
42452         var btn = Roo.DomHelper.append(parentEl, {
42453             tag: "div",
42454             cls: "x-layout-tools-button",
42455             children: [ {
42456                 tag: "div",
42457                 cls: "roo-layout-tools-button-inner " + className,
42458                 html: "&#160;"
42459             }]
42460         }, true);
42461         btn.addClassOnOver("roo-layout-tools-button-over");
42462         return btn;
42463     }
42464 });/*
42465  * Based on:
42466  * Ext JS Library 1.1.1
42467  * Copyright(c) 2006-2007, Ext JS, LLC.
42468  *
42469  * Originally Released Under LGPL - original licence link has changed is not relivant.
42470  *
42471  * Fork - LGPL
42472  * <script type="text/javascript">
42473  */
42474  
42475
42476
42477 /**
42478  * @class Roo.SplitLayoutRegion
42479  * @extends Roo.LayoutRegion
42480  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42481  */
42482 Roo.bootstrap.layout.Split = function(config){
42483     this.cursor = config.cursor;
42484     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42485 };
42486
42487 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42488 {
42489     splitTip : "Drag to resize.",
42490     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42491     useSplitTips : false,
42492
42493     applyConfig : function(config){
42494         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42495     },
42496     
42497     onRender : function(ctr,pos) {
42498         
42499         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42500         if(!this.config.split){
42501             return;
42502         }
42503         if(!this.split){
42504             
42505             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42506                             tag: "div",
42507                             id: this.el.id + "-split",
42508                             cls: "roo-layout-split roo-layout-split-"+this.position,
42509                             html: "&#160;"
42510             });
42511             /** The SplitBar for this region 
42512             * @type Roo.SplitBar */
42513             // does not exist yet...
42514             Roo.log([this.position, this.orientation]);
42515             
42516             this.split = new Roo.bootstrap.SplitBar({
42517                 dragElement : splitEl,
42518                 resizingElement: this.el,
42519                 orientation : this.orientation
42520             });
42521             
42522             this.split.on("moved", this.onSplitMove, this);
42523             this.split.useShim = this.config.useShim === true;
42524             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42525             if(this.useSplitTips){
42526                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42527             }
42528             //if(config.collapsible){
42529             //    this.split.el.on("dblclick", this.collapse,  this);
42530             //}
42531         }
42532         if(typeof this.config.minSize != "undefined"){
42533             this.split.minSize = this.config.minSize;
42534         }
42535         if(typeof this.config.maxSize != "undefined"){
42536             this.split.maxSize = this.config.maxSize;
42537         }
42538         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42539             this.hideSplitter();
42540         }
42541         
42542     },
42543
42544     getHMaxSize : function(){
42545          var cmax = this.config.maxSize || 10000;
42546          var center = this.mgr.getRegion("center");
42547          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42548     },
42549
42550     getVMaxSize : function(){
42551          var cmax = this.config.maxSize || 10000;
42552          var center = this.mgr.getRegion("center");
42553          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42554     },
42555
42556     onSplitMove : function(split, newSize){
42557         this.fireEvent("resized", this, newSize);
42558     },
42559     
42560     /** 
42561      * Returns the {@link Roo.SplitBar} for this region.
42562      * @return {Roo.SplitBar}
42563      */
42564     getSplitBar : function(){
42565         return this.split;
42566     },
42567     
42568     hide : function(){
42569         this.hideSplitter();
42570         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42571     },
42572
42573     hideSplitter : function(){
42574         if(this.split){
42575             this.split.el.setLocation(-2000,-2000);
42576             this.split.el.hide();
42577         }
42578     },
42579
42580     show : function(){
42581         if(this.split){
42582             this.split.el.show();
42583         }
42584         Roo.bootstrap.layout.Split.superclass.show.call(this);
42585     },
42586     
42587     beforeSlide: function(){
42588         if(Roo.isGecko){// firefox overflow auto bug workaround
42589             this.bodyEl.clip();
42590             if(this.tabs) {
42591                 this.tabs.bodyEl.clip();
42592             }
42593             if(this.activePanel){
42594                 this.activePanel.getEl().clip();
42595                 
42596                 if(this.activePanel.beforeSlide){
42597                     this.activePanel.beforeSlide();
42598                 }
42599             }
42600         }
42601     },
42602     
42603     afterSlide : function(){
42604         if(Roo.isGecko){// firefox overflow auto bug workaround
42605             this.bodyEl.unclip();
42606             if(this.tabs) {
42607                 this.tabs.bodyEl.unclip();
42608             }
42609             if(this.activePanel){
42610                 this.activePanel.getEl().unclip();
42611                 if(this.activePanel.afterSlide){
42612                     this.activePanel.afterSlide();
42613                 }
42614             }
42615         }
42616     },
42617
42618     initAutoHide : function(){
42619         if(this.autoHide !== false){
42620             if(!this.autoHideHd){
42621                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42622                 this.autoHideHd = {
42623                     "mouseout": function(e){
42624                         if(!e.within(this.el, true)){
42625                             st.delay(500);
42626                         }
42627                     },
42628                     "mouseover" : function(e){
42629                         st.cancel();
42630                     },
42631                     scope : this
42632                 };
42633             }
42634             this.el.on(this.autoHideHd);
42635         }
42636     },
42637
42638     clearAutoHide : function(){
42639         if(this.autoHide !== false){
42640             this.el.un("mouseout", this.autoHideHd.mouseout);
42641             this.el.un("mouseover", this.autoHideHd.mouseover);
42642         }
42643     },
42644
42645     clearMonitor : function(){
42646         Roo.get(document).un("click", this.slideInIf, this);
42647     },
42648
42649     // these names are backwards but not changed for compat
42650     slideOut : function(){
42651         if(this.isSlid || this.el.hasActiveFx()){
42652             return;
42653         }
42654         this.isSlid = true;
42655         if(this.collapseBtn){
42656             this.collapseBtn.hide();
42657         }
42658         this.closeBtnState = this.closeBtn.getStyle('display');
42659         this.closeBtn.hide();
42660         if(this.stickBtn){
42661             this.stickBtn.show();
42662         }
42663         this.el.show();
42664         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42665         this.beforeSlide();
42666         this.el.setStyle("z-index", 10001);
42667         this.el.slideIn(this.getSlideAnchor(), {
42668             callback: function(){
42669                 this.afterSlide();
42670                 this.initAutoHide();
42671                 Roo.get(document).on("click", this.slideInIf, this);
42672                 this.fireEvent("slideshow", this);
42673             },
42674             scope: this,
42675             block: true
42676         });
42677     },
42678
42679     afterSlideIn : function(){
42680         this.clearAutoHide();
42681         this.isSlid = false;
42682         this.clearMonitor();
42683         this.el.setStyle("z-index", "");
42684         if(this.collapseBtn){
42685             this.collapseBtn.show();
42686         }
42687         this.closeBtn.setStyle('display', this.closeBtnState);
42688         if(this.stickBtn){
42689             this.stickBtn.hide();
42690         }
42691         this.fireEvent("slidehide", this);
42692     },
42693
42694     slideIn : function(cb){
42695         if(!this.isSlid || this.el.hasActiveFx()){
42696             Roo.callback(cb);
42697             return;
42698         }
42699         this.isSlid = false;
42700         this.beforeSlide();
42701         this.el.slideOut(this.getSlideAnchor(), {
42702             callback: function(){
42703                 this.el.setLeftTop(-10000, -10000);
42704                 this.afterSlide();
42705                 this.afterSlideIn();
42706                 Roo.callback(cb);
42707             },
42708             scope: this,
42709             block: true
42710         });
42711     },
42712     
42713     slideInIf : function(e){
42714         if(!e.within(this.el)){
42715             this.slideIn();
42716         }
42717     },
42718
42719     animateCollapse : function(){
42720         this.beforeSlide();
42721         this.el.setStyle("z-index", 20000);
42722         var anchor = this.getSlideAnchor();
42723         this.el.slideOut(anchor, {
42724             callback : function(){
42725                 this.el.setStyle("z-index", "");
42726                 this.collapsedEl.slideIn(anchor, {duration:.3});
42727                 this.afterSlide();
42728                 this.el.setLocation(-10000,-10000);
42729                 this.el.hide();
42730                 this.fireEvent("collapsed", this);
42731             },
42732             scope: this,
42733             block: true
42734         });
42735     },
42736
42737     animateExpand : function(){
42738         this.beforeSlide();
42739         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42740         this.el.setStyle("z-index", 20000);
42741         this.collapsedEl.hide({
42742             duration:.1
42743         });
42744         this.el.slideIn(this.getSlideAnchor(), {
42745             callback : function(){
42746                 this.el.setStyle("z-index", "");
42747                 this.afterSlide();
42748                 if(this.split){
42749                     this.split.el.show();
42750                 }
42751                 this.fireEvent("invalidated", this);
42752                 this.fireEvent("expanded", this);
42753             },
42754             scope: this,
42755             block: true
42756         });
42757     },
42758
42759     anchors : {
42760         "west" : "left",
42761         "east" : "right",
42762         "north" : "top",
42763         "south" : "bottom"
42764     },
42765
42766     sanchors : {
42767         "west" : "l",
42768         "east" : "r",
42769         "north" : "t",
42770         "south" : "b"
42771     },
42772
42773     canchors : {
42774         "west" : "tl-tr",
42775         "east" : "tr-tl",
42776         "north" : "tl-bl",
42777         "south" : "bl-tl"
42778     },
42779
42780     getAnchor : function(){
42781         return this.anchors[this.position];
42782     },
42783
42784     getCollapseAnchor : function(){
42785         return this.canchors[this.position];
42786     },
42787
42788     getSlideAnchor : function(){
42789         return this.sanchors[this.position];
42790     },
42791
42792     getAlignAdj : function(){
42793         var cm = this.cmargins;
42794         switch(this.position){
42795             case "west":
42796                 return [0, 0];
42797             break;
42798             case "east":
42799                 return [0, 0];
42800             break;
42801             case "north":
42802                 return [0, 0];
42803             break;
42804             case "south":
42805                 return [0, 0];
42806             break;
42807         }
42808     },
42809
42810     getExpandAdj : function(){
42811         var c = this.collapsedEl, cm = this.cmargins;
42812         switch(this.position){
42813             case "west":
42814                 return [-(cm.right+c.getWidth()+cm.left), 0];
42815             break;
42816             case "east":
42817                 return [cm.right+c.getWidth()+cm.left, 0];
42818             break;
42819             case "north":
42820                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42821             break;
42822             case "south":
42823                 return [0, cm.top+cm.bottom+c.getHeight()];
42824             break;
42825         }
42826     }
42827 });/*
42828  * Based on:
42829  * Ext JS Library 1.1.1
42830  * Copyright(c) 2006-2007, Ext JS, LLC.
42831  *
42832  * Originally Released Under LGPL - original licence link has changed is not relivant.
42833  *
42834  * Fork - LGPL
42835  * <script type="text/javascript">
42836  */
42837 /*
42838  * These classes are private internal classes
42839  */
42840 Roo.bootstrap.layout.Center = function(config){
42841     config.region = "center";
42842     Roo.bootstrap.layout.Region.call(this, config);
42843     this.visible = true;
42844     this.minWidth = config.minWidth || 20;
42845     this.minHeight = config.minHeight || 20;
42846 };
42847
42848 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42849     hide : function(){
42850         // center panel can't be hidden
42851     },
42852     
42853     show : function(){
42854         // center panel can't be hidden
42855     },
42856     
42857     getMinWidth: function(){
42858         return this.minWidth;
42859     },
42860     
42861     getMinHeight: function(){
42862         return this.minHeight;
42863     }
42864 });
42865
42866
42867
42868
42869  
42870
42871
42872
42873
42874
42875
42876 Roo.bootstrap.layout.North = function(config)
42877 {
42878     config.region = 'north';
42879     config.cursor = 'n-resize';
42880     
42881     Roo.bootstrap.layout.Split.call(this, config);
42882     
42883     
42884     if(this.split){
42885         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42886         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42887         this.split.el.addClass("roo-layout-split-v");
42888     }
42889     //var size = config.initialSize || config.height;
42890     //if(this.el && typeof size != "undefined"){
42891     //    this.el.setHeight(size);
42892     //}
42893 };
42894 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42895 {
42896     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42897      
42898      
42899     onRender : function(ctr, pos)
42900     {
42901         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42902         var size = this.config.initialSize || this.config.height;
42903         if(this.el && typeof size != "undefined"){
42904             this.el.setHeight(size);
42905         }
42906     
42907     },
42908     
42909     getBox : function(){
42910         if(this.collapsed){
42911             return this.collapsedEl.getBox();
42912         }
42913         var box = this.el.getBox();
42914         if(this.split){
42915             box.height += this.split.el.getHeight();
42916         }
42917         return box;
42918     },
42919     
42920     updateBox : function(box){
42921         if(this.split && !this.collapsed){
42922             box.height -= this.split.el.getHeight();
42923             this.split.el.setLeft(box.x);
42924             this.split.el.setTop(box.y+box.height);
42925             this.split.el.setWidth(box.width);
42926         }
42927         if(this.collapsed){
42928             this.updateBody(box.width, null);
42929         }
42930         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42931     }
42932 });
42933
42934
42935
42936
42937
42938 Roo.bootstrap.layout.South = function(config){
42939     config.region = 'south';
42940     config.cursor = 's-resize';
42941     Roo.bootstrap.layout.Split.call(this, config);
42942     if(this.split){
42943         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42944         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42945         this.split.el.addClass("roo-layout-split-v");
42946     }
42947     
42948 };
42949
42950 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42951     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42952     
42953     onRender : function(ctr, pos)
42954     {
42955         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42956         var size = this.config.initialSize || this.config.height;
42957         if(this.el && typeof size != "undefined"){
42958             this.el.setHeight(size);
42959         }
42960     
42961     },
42962     
42963     getBox : function(){
42964         if(this.collapsed){
42965             return this.collapsedEl.getBox();
42966         }
42967         var box = this.el.getBox();
42968         if(this.split){
42969             var sh = this.split.el.getHeight();
42970             box.height += sh;
42971             box.y -= sh;
42972         }
42973         return box;
42974     },
42975     
42976     updateBox : function(box){
42977         if(this.split && !this.collapsed){
42978             var sh = this.split.el.getHeight();
42979             box.height -= sh;
42980             box.y += sh;
42981             this.split.el.setLeft(box.x);
42982             this.split.el.setTop(box.y-sh);
42983             this.split.el.setWidth(box.width);
42984         }
42985         if(this.collapsed){
42986             this.updateBody(box.width, null);
42987         }
42988         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42989     }
42990 });
42991
42992 Roo.bootstrap.layout.East = function(config){
42993     config.region = "east";
42994     config.cursor = "e-resize";
42995     Roo.bootstrap.layout.Split.call(this, config);
42996     if(this.split){
42997         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42998         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42999         this.split.el.addClass("roo-layout-split-h");
43000     }
43001     
43002 };
43003 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43004     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43005     
43006     onRender : function(ctr, pos)
43007     {
43008         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43009         var size = this.config.initialSize || this.config.width;
43010         if(this.el && typeof size != "undefined"){
43011             this.el.setWidth(size);
43012         }
43013     
43014     },
43015     
43016     getBox : function(){
43017         if(this.collapsed){
43018             return this.collapsedEl.getBox();
43019         }
43020         var box = this.el.getBox();
43021         if(this.split){
43022             var sw = this.split.el.getWidth();
43023             box.width += sw;
43024             box.x -= sw;
43025         }
43026         return box;
43027     },
43028
43029     updateBox : function(box){
43030         if(this.split && !this.collapsed){
43031             var sw = this.split.el.getWidth();
43032             box.width -= sw;
43033             this.split.el.setLeft(box.x);
43034             this.split.el.setTop(box.y);
43035             this.split.el.setHeight(box.height);
43036             box.x += sw;
43037         }
43038         if(this.collapsed){
43039             this.updateBody(null, box.height);
43040         }
43041         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43042     }
43043 });
43044
43045 Roo.bootstrap.layout.West = function(config){
43046     config.region = "west";
43047     config.cursor = "w-resize";
43048     
43049     Roo.bootstrap.layout.Split.call(this, config);
43050     if(this.split){
43051         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43052         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43053         this.split.el.addClass("roo-layout-split-h");
43054     }
43055     
43056 };
43057 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43058     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43059     
43060     onRender: function(ctr, pos)
43061     {
43062         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43063         var size = this.config.initialSize || this.config.width;
43064         if(typeof size != "undefined"){
43065             this.el.setWidth(size);
43066         }
43067     },
43068     
43069     getBox : function(){
43070         if(this.collapsed){
43071             return this.collapsedEl.getBox();
43072         }
43073         var box = this.el.getBox();
43074         if (box.width == 0) {
43075             box.width = this.config.width; // kludge?
43076         }
43077         if(this.split){
43078             box.width += this.split.el.getWidth();
43079         }
43080         return box;
43081     },
43082     
43083     updateBox : function(box){
43084         if(this.split && !this.collapsed){
43085             var sw = this.split.el.getWidth();
43086             box.width -= sw;
43087             this.split.el.setLeft(box.x+box.width);
43088             this.split.el.setTop(box.y);
43089             this.split.el.setHeight(box.height);
43090         }
43091         if(this.collapsed){
43092             this.updateBody(null, box.height);
43093         }
43094         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43095     }
43096 });/*
43097  * Based on:
43098  * Ext JS Library 1.1.1
43099  * Copyright(c) 2006-2007, Ext JS, LLC.
43100  *
43101  * Originally Released Under LGPL - original licence link has changed is not relivant.
43102  *
43103  * Fork - LGPL
43104  * <script type="text/javascript">
43105  */
43106 /**
43107  * @class Roo.bootstrap.paenl.Content
43108  * @extends Roo.util.Observable
43109  * @children Roo.bootstrap.Component
43110  * @parent builder Roo.bootstrap.layout.Border
43111  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43112  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
43113  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
43114  * @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
43115  * @cfg {Boolean}   closable      True if the panel can be closed/removed
43116  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
43117  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43118  * @cfg {Toolbar}   toolbar       A toolbar for this panel
43119  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
43120  * @cfg {String} title          The title for this panel
43121  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43122  * @cfg {String} url            Calls {@link #setUrl} with this value
43123  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43124  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
43125  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
43126  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
43127  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
43128  * @cfg {Boolean} badges render the badges
43129  * @cfg {String} cls  extra classes to use  
43130  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43131  
43132  * @constructor
43133  * Create a new ContentPanel.
43134  * @param {String/Object} config A string to set only the title or a config object
43135  
43136  */
43137 Roo.bootstrap.panel.Content = function( config){
43138     
43139     this.tpl = config.tpl || false;
43140     
43141     var el = config.el;
43142     var content = config.content;
43143
43144     if(config.autoCreate){ // xtype is available if this is called from factory
43145         el = Roo.id();
43146     }
43147     this.el = Roo.get(el);
43148     if(!this.el && config && config.autoCreate){
43149         if(typeof config.autoCreate == "object"){
43150             if(!config.autoCreate.id){
43151                 config.autoCreate.id = config.id||el;
43152             }
43153             this.el = Roo.DomHelper.append(document.body,
43154                         config.autoCreate, true);
43155         }else{
43156             var elcfg =  {
43157                 tag: "div",
43158                 cls: (config.cls || '') +
43159                     (config.background ? ' bg-' + config.background : '') +
43160                     " roo-layout-inactive-content",
43161                 id: config.id||el
43162             };
43163             if (config.iframe) {
43164                 elcfg.cn = [
43165                     {
43166                         tag : 'iframe',
43167                         style : 'border: 0px',
43168                         src : 'about:blank'
43169                     }
43170                 ];
43171             }
43172               
43173             if (config.html) {
43174                 elcfg.html = config.html;
43175                 
43176             }
43177                         
43178             this.el = Roo.DomHelper.append(document.body, elcfg , true);
43179             if (config.iframe) {
43180                 this.iframeEl = this.el.select('iframe',true).first();
43181             }
43182             
43183         }
43184     } 
43185     this.closable = false;
43186     this.loaded = false;
43187     this.active = false;
43188    
43189       
43190     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43191         
43192         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43193         
43194         this.wrapEl = this.el; //this.el.wrap();
43195         var ti = [];
43196         if (config.toolbar.items) {
43197             ti = config.toolbar.items ;
43198             delete config.toolbar.items ;
43199         }
43200         
43201         var nitems = [];
43202         this.toolbar.render(this.wrapEl, 'before');
43203         for(var i =0;i < ti.length;i++) {
43204           //  Roo.log(['add child', items[i]]);
43205             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43206         }
43207         this.toolbar.items = nitems;
43208         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43209         delete config.toolbar;
43210         
43211     }
43212     /*
43213     // xtype created footer. - not sure if will work as we normally have to render first..
43214     if (this.footer && !this.footer.el && this.footer.xtype) {
43215         if (!this.wrapEl) {
43216             this.wrapEl = this.el.wrap();
43217         }
43218     
43219         this.footer.container = this.wrapEl.createChild();
43220          
43221         this.footer = Roo.factory(this.footer, Roo);
43222         
43223     }
43224     */
43225     
43226      if(typeof config == "string"){
43227         this.title = config;
43228     }else{
43229         Roo.apply(this, config);
43230     }
43231     
43232     if(this.resizeEl){
43233         this.resizeEl = Roo.get(this.resizeEl, true);
43234     }else{
43235         this.resizeEl = this.el;
43236     }
43237     // handle view.xtype
43238     
43239  
43240     
43241     
43242     this.addEvents({
43243         /**
43244          * @event activate
43245          * Fires when this panel is activated. 
43246          * @param {Roo.ContentPanel} this
43247          */
43248         "activate" : true,
43249         /**
43250          * @event deactivate
43251          * Fires when this panel is activated. 
43252          * @param {Roo.ContentPanel} this
43253          */
43254         "deactivate" : true,
43255
43256         /**
43257          * @event resize
43258          * Fires when this panel is resized if fitToFrame is true.
43259          * @param {Roo.ContentPanel} this
43260          * @param {Number} width The width after any component adjustments
43261          * @param {Number} height The height after any component adjustments
43262          */
43263         "resize" : true,
43264         
43265          /**
43266          * @event render
43267          * Fires when this tab is created
43268          * @param {Roo.ContentPanel} this
43269          */
43270         "render" : true,
43271         
43272           /**
43273          * @event scroll
43274          * Fires when this content is scrolled
43275          * @param {Roo.ContentPanel} this
43276          * @param {Event} scrollEvent
43277          */
43278         "scroll" : true
43279         
43280         
43281         
43282     });
43283     
43284
43285     
43286     
43287     if(this.autoScroll && !this.iframe){
43288         this.resizeEl.setStyle("overflow", "auto");
43289         this.resizeEl.on('scroll', this.onScroll, this);
43290     } else {
43291         // fix randome scrolling
43292         //this.el.on('scroll', function() {
43293         //    Roo.log('fix random scolling');
43294         //    this.scrollTo('top',0); 
43295         //});
43296     }
43297     content = content || this.content;
43298     if(content){
43299         this.setContent(content);
43300     }
43301     if(config && config.url){
43302         this.setUrl(this.url, this.params, this.loadOnce);
43303     }
43304     
43305     
43306     
43307     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43308     
43309     if (this.view && typeof(this.view.xtype) != 'undefined') {
43310         this.view.el = this.el.appendChild(document.createElement("div"));
43311         this.view = Roo.factory(this.view); 
43312         this.view.render  &&  this.view.render(false, '');  
43313     }
43314     
43315     
43316     this.fireEvent('render', this);
43317 };
43318
43319 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43320     
43321     cls : '',
43322     background : '',
43323     
43324     tabTip : '',
43325     
43326     iframe : false,
43327     iframeEl : false,
43328     
43329     /* Resize Element - use this to work out scroll etc. */
43330     resizeEl : false,
43331     
43332     setRegion : function(region){
43333         this.region = region;
43334         this.setActiveClass(region && !this.background);
43335     },
43336     
43337     
43338     setActiveClass: function(state)
43339     {
43340         if(state){
43341            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43342            this.el.setStyle('position','relative');
43343         }else{
43344            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43345            this.el.setStyle('position', 'absolute');
43346         } 
43347     },
43348     
43349     /**
43350      * Returns the toolbar for this Panel if one was configured. 
43351      * @return {Roo.Toolbar} 
43352      */
43353     getToolbar : function(){
43354         return this.toolbar;
43355     },
43356     
43357     setActiveState : function(active)
43358     {
43359         this.active = active;
43360         this.setActiveClass(active);
43361         if(!active){
43362             if(this.fireEvent("deactivate", this) === false){
43363                 return false;
43364             }
43365             return true;
43366         }
43367         this.fireEvent("activate", this);
43368         return true;
43369     },
43370     /**
43371      * Updates this panel's element (not for iframe)
43372      * @param {String} content The new content
43373      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43374     */
43375     setContent : function(content, loadScripts){
43376         if (this.iframe) {
43377             return;
43378         }
43379         
43380         this.el.update(content, loadScripts);
43381     },
43382
43383     ignoreResize : function(w, h)
43384     {
43385         //return false; // always resize?
43386         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43387             return true;
43388         }else{
43389             this.lastSize = {width: w, height: h};
43390             return false;
43391         }
43392     },
43393     /**
43394      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43395      * @return {Roo.UpdateManager} The UpdateManager
43396      */
43397     getUpdateManager : function(){
43398         if (this.iframe) {
43399             return false;
43400         }
43401         return this.el.getUpdateManager();
43402     },
43403      /**
43404      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43405      * Does not work with IFRAME contents
43406      * @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:
43407 <pre><code>
43408 panel.load({
43409     url: "your-url.php",
43410     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43411     callback: yourFunction,
43412     scope: yourObject, //(optional scope)
43413     discardUrl: false,
43414     nocache: false,
43415     text: "Loading...",
43416     timeout: 30,
43417     scripts: false
43418 });
43419 </code></pre>
43420      
43421      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43422      * 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.
43423      * @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}
43424      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43425      * @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.
43426      * @return {Roo.ContentPanel} this
43427      */
43428     load : function(){
43429         
43430         if (this.iframe) {
43431             return this;
43432         }
43433         
43434         var um = this.el.getUpdateManager();
43435         um.update.apply(um, arguments);
43436         return this;
43437     },
43438
43439
43440     /**
43441      * 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.
43442      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43443      * @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)
43444      * @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)
43445      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43446      */
43447     setUrl : function(url, params, loadOnce){
43448         if (this.iframe) {
43449             this.iframeEl.dom.src = url;
43450             return false;
43451         }
43452         
43453         if(this.refreshDelegate){
43454             this.removeListener("activate", this.refreshDelegate);
43455         }
43456         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43457         this.on("activate", this.refreshDelegate);
43458         return this.el.getUpdateManager();
43459     },
43460     
43461     _handleRefresh : function(url, params, loadOnce){
43462         if(!loadOnce || !this.loaded){
43463             var updater = this.el.getUpdateManager();
43464             updater.update(url, params, this._setLoaded.createDelegate(this));
43465         }
43466     },
43467     
43468     _setLoaded : function(){
43469         this.loaded = true;
43470     }, 
43471     
43472     /**
43473      * Returns this panel's id
43474      * @return {String} 
43475      */
43476     getId : function(){
43477         return this.el.id;
43478     },
43479     
43480     /** 
43481      * Returns this panel's element - used by regiosn to add.
43482      * @return {Roo.Element} 
43483      */
43484     getEl : function(){
43485         return this.wrapEl || this.el;
43486     },
43487     
43488    
43489     
43490     adjustForComponents : function(width, height)
43491     {
43492         //Roo.log('adjustForComponents ');
43493         if(this.resizeEl != this.el){
43494             width -= this.el.getFrameWidth('lr');
43495             height -= this.el.getFrameWidth('tb');
43496         }
43497         if(this.toolbar){
43498             var te = this.toolbar.getEl();
43499             te.setWidth(width);
43500             height -= te.getHeight();
43501         }
43502         if(this.footer){
43503             var te = this.footer.getEl();
43504             te.setWidth(width);
43505             height -= te.getHeight();
43506         }
43507         
43508         
43509         if(this.adjustments){
43510             width += this.adjustments[0];
43511             height += this.adjustments[1];
43512         }
43513         return {"width": width, "height": height};
43514     },
43515     
43516     setSize : function(width, height){
43517         if(this.fitToFrame && !this.ignoreResize(width, height)){
43518             if(this.fitContainer && this.resizeEl != this.el){
43519                 this.el.setSize(width, height);
43520             }
43521             var size = this.adjustForComponents(width, height);
43522             if (this.iframe) {
43523                 this.iframeEl.setSize(width,height);
43524             }
43525             
43526             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43527             this.fireEvent('resize', this, size.width, size.height);
43528             
43529             
43530         }
43531     },
43532     
43533     /**
43534      * Returns this panel's title
43535      * @return {String} 
43536      */
43537     getTitle : function(){
43538         
43539         if (typeof(this.title) != 'object') {
43540             return this.title;
43541         }
43542         
43543         var t = '';
43544         for (var k in this.title) {
43545             if (!this.title.hasOwnProperty(k)) {
43546                 continue;
43547             }
43548             
43549             if (k.indexOf('-') >= 0) {
43550                 var s = k.split('-');
43551                 for (var i = 0; i<s.length; i++) {
43552                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43553                 }
43554             } else {
43555                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43556             }
43557         }
43558         return t;
43559     },
43560     
43561     /**
43562      * Set this panel's title
43563      * @param {String} title
43564      */
43565     setTitle : function(title){
43566         this.title = title;
43567         if(this.region){
43568             this.region.updatePanelTitle(this, title);
43569         }
43570     },
43571     
43572     /**
43573      * Returns true is this panel was configured to be closable
43574      * @return {Boolean} 
43575      */
43576     isClosable : function(){
43577         return this.closable;
43578     },
43579     
43580     beforeSlide : function(){
43581         this.el.clip();
43582         this.resizeEl.clip();
43583     },
43584     
43585     afterSlide : function(){
43586         this.el.unclip();
43587         this.resizeEl.unclip();
43588     },
43589     
43590     /**
43591      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43592      *   Will fail silently if the {@link #setUrl} method has not been called.
43593      *   This does not activate the panel, just updates its content.
43594      */
43595     refresh : function(){
43596         if(this.refreshDelegate){
43597            this.loaded = false;
43598            this.refreshDelegate();
43599         }
43600     },
43601     
43602     /**
43603      * Destroys this panel
43604      */
43605     destroy : function(){
43606         this.el.removeAllListeners();
43607         var tempEl = document.createElement("span");
43608         tempEl.appendChild(this.el.dom);
43609         tempEl.innerHTML = "";
43610         this.el.remove();
43611         this.el = null;
43612     },
43613     
43614     /**
43615      * form - if the content panel contains a form - this is a reference to it.
43616      * @type {Roo.form.Form}
43617      */
43618     form : false,
43619     /**
43620      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43621      *    This contains a reference to it.
43622      * @type {Roo.View}
43623      */
43624     view : false,
43625     
43626       /**
43627      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43628      * <pre><code>
43629
43630 layout.addxtype({
43631        xtype : 'Form',
43632        items: [ .... ]
43633    }
43634 );
43635
43636 </code></pre>
43637      * @param {Object} cfg Xtype definition of item to add.
43638      */
43639     
43640     
43641     getChildContainer: function () {
43642         return this.getEl();
43643     },
43644     
43645     
43646     onScroll : function(e)
43647     {
43648         this.fireEvent('scroll', this, e);
43649     }
43650     
43651     
43652     /*
43653         var  ret = new Roo.factory(cfg);
43654         return ret;
43655         
43656         
43657         // add form..
43658         if (cfg.xtype.match(/^Form$/)) {
43659             
43660             var el;
43661             //if (this.footer) {
43662             //    el = this.footer.container.insertSibling(false, 'before');
43663             //} else {
43664                 el = this.el.createChild();
43665             //}
43666
43667             this.form = new  Roo.form.Form(cfg);
43668             
43669             
43670             if ( this.form.allItems.length) {
43671                 this.form.render(el.dom);
43672             }
43673             return this.form;
43674         }
43675         // should only have one of theses..
43676         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43677             // views.. should not be just added - used named prop 'view''
43678             
43679             cfg.el = this.el.appendChild(document.createElement("div"));
43680             // factory?
43681             
43682             var ret = new Roo.factory(cfg);
43683              
43684              ret.render && ret.render(false, ''); // render blank..
43685             this.view = ret;
43686             return ret;
43687         }
43688         return false;
43689     }
43690     \*/
43691 });
43692  
43693 /**
43694  * @class Roo.bootstrap.panel.Grid
43695  * @extends Roo.bootstrap.panel.Content
43696  * @constructor
43697  * Create a new GridPanel.
43698  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43699  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43700  * @param {Object} config A the config object
43701   
43702  */
43703
43704
43705
43706 Roo.bootstrap.panel.Grid = function(config)
43707 {
43708     
43709       
43710     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43711         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43712
43713     config.el = this.wrapper;
43714     //this.el = this.wrapper;
43715     
43716       if (config.container) {
43717         // ctor'ed from a Border/panel.grid
43718         
43719         
43720         this.wrapper.setStyle("overflow", "hidden");
43721         this.wrapper.addClass('roo-grid-container');
43722
43723     }
43724     
43725     
43726     if(config.toolbar){
43727         var tool_el = this.wrapper.createChild();    
43728         this.toolbar = Roo.factory(config.toolbar);
43729         var ti = [];
43730         if (config.toolbar.items) {
43731             ti = config.toolbar.items ;
43732             delete config.toolbar.items ;
43733         }
43734         
43735         var nitems = [];
43736         this.toolbar.render(tool_el);
43737         for(var i =0;i < ti.length;i++) {
43738           //  Roo.log(['add child', items[i]]);
43739             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43740         }
43741         this.toolbar.items = nitems;
43742         
43743         delete config.toolbar;
43744     }
43745     
43746     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43747     config.grid.scrollBody = true;;
43748     config.grid.monitorWindowResize = false; // turn off autosizing
43749     config.grid.autoHeight = false;
43750     config.grid.autoWidth = false;
43751     
43752     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43753     
43754     if (config.background) {
43755         // render grid on panel activation (if panel background)
43756         this.on('activate', function(gp) {
43757             if (!gp.grid.rendered) {
43758                 gp.grid.render(this.wrapper);
43759                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43760             }
43761         });
43762             
43763     } else {
43764         this.grid.render(this.wrapper);
43765         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43766
43767     }
43768     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43769     // ??? needed ??? config.el = this.wrapper;
43770     
43771     
43772     
43773   
43774     // xtype created footer. - not sure if will work as we normally have to render first..
43775     if (this.footer && !this.footer.el && this.footer.xtype) {
43776         
43777         var ctr = this.grid.getView().getFooterPanel(true);
43778         this.footer.dataSource = this.grid.dataSource;
43779         this.footer = Roo.factory(this.footer, Roo);
43780         this.footer.render(ctr);
43781         
43782     }
43783     
43784     
43785     
43786     
43787      
43788 };
43789
43790 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43791 {
43792   
43793     getId : function(){
43794         return this.grid.id;
43795     },
43796     
43797     /**
43798      * Returns the grid for this panel
43799      * @return {Roo.bootstrap.Table} 
43800      */
43801     getGrid : function(){
43802         return this.grid;    
43803     },
43804     
43805     setSize : function(width, height)
43806     {
43807      
43808         //if(!this.ignoreResize(width, height)){
43809             var grid = this.grid;
43810             var size = this.adjustForComponents(width, height);
43811             // tfoot is not a footer?
43812           
43813             
43814             var gridel = grid.getGridEl();
43815             gridel.setSize(size.width, size.height);
43816             
43817             var tbd = grid.getGridEl().select('tbody', true).first();
43818             var thd = grid.getGridEl().select('thead',true).first();
43819             var tbf= grid.getGridEl().select('tfoot', true).first();
43820
43821             if (tbf) {
43822                 size.height -= tbf.getHeight();
43823             }
43824             if (thd) {
43825                 size.height -= thd.getHeight();
43826             }
43827             
43828             tbd.setSize(size.width, size.height );
43829             // this is for the account management tab -seems to work there.
43830             var thd = grid.getGridEl().select('thead',true).first();
43831             //if (tbd) {
43832             //    tbd.setSize(size.width, size.height - thd.getHeight());
43833             //}
43834              
43835             grid.autoSize();
43836         //}
43837    
43838     },
43839      
43840     
43841     
43842     beforeSlide : function(){
43843         this.grid.getView().scroller.clip();
43844     },
43845     
43846     afterSlide : function(){
43847         this.grid.getView().scroller.unclip();
43848     },
43849     
43850     destroy : function(){
43851         this.grid.destroy();
43852         delete this.grid;
43853         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43854     }
43855 });
43856
43857 /**
43858  * @class Roo.bootstrap.panel.Nest
43859  * @extends Roo.bootstrap.panel.Content
43860  * @constructor
43861  * Create a new Panel, that can contain a layout.Border.
43862  * 
43863  * 
43864  * @param {String/Object} config A string to set only the title or a config object
43865  */
43866 Roo.bootstrap.panel.Nest = function(config)
43867 {
43868     // construct with only one argument..
43869     /* FIXME - implement nicer consturctors
43870     if (layout.layout) {
43871         config = layout;
43872         layout = config.layout;
43873         delete config.layout;
43874     }
43875     if (layout.xtype && !layout.getEl) {
43876         // then layout needs constructing..
43877         layout = Roo.factory(layout, Roo);
43878     }
43879     */
43880     
43881     config.el =  config.layout.getEl();
43882     
43883     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43884     
43885     config.layout.monitorWindowResize = false; // turn off autosizing
43886     this.layout = config.layout;
43887     this.layout.getEl().addClass("roo-layout-nested-layout");
43888     this.layout.parent = this;
43889     
43890     
43891     
43892     
43893 };
43894
43895 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43896     /**
43897     * @cfg {Roo.BorderLayout} layout The layout for this panel
43898     */
43899     layout : false,
43900
43901     setSize : function(width, height){
43902         if(!this.ignoreResize(width, height)){
43903             var size = this.adjustForComponents(width, height);
43904             var el = this.layout.getEl();
43905             if (size.height < 1) {
43906                 el.setWidth(size.width);   
43907             } else {
43908                 el.setSize(size.width, size.height);
43909             }
43910             var touch = el.dom.offsetWidth;
43911             this.layout.layout();
43912             // ie requires a double layout on the first pass
43913             if(Roo.isIE && !this.initialized){
43914                 this.initialized = true;
43915                 this.layout.layout();
43916             }
43917         }
43918     },
43919     
43920     // activate all subpanels if not currently active..
43921     
43922     setActiveState : function(active){
43923         this.active = active;
43924         this.setActiveClass(active);
43925         
43926         if(!active){
43927             this.fireEvent("deactivate", this);
43928             return;
43929         }
43930         
43931         this.fireEvent("activate", this);
43932         // not sure if this should happen before or after..
43933         if (!this.layout) {
43934             return; // should not happen..
43935         }
43936         var reg = false;
43937         for (var r in this.layout.regions) {
43938             reg = this.layout.getRegion(r);
43939             if (reg.getActivePanel()) {
43940                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43941                 reg.setActivePanel(reg.getActivePanel());
43942                 continue;
43943             }
43944             if (!reg.panels.length) {
43945                 continue;
43946             }
43947             reg.showPanel(reg.getPanel(0));
43948         }
43949         
43950         
43951         
43952         
43953     },
43954     
43955     /**
43956      * Returns the nested BorderLayout for this panel
43957      * @return {Roo.BorderLayout} 
43958      */
43959     getLayout : function(){
43960         return this.layout;
43961     },
43962     
43963      /**
43964      * Adds a xtype elements to the layout of the nested panel
43965      * <pre><code>
43966
43967 panel.addxtype({
43968        xtype : 'ContentPanel',
43969        region: 'west',
43970        items: [ .... ]
43971    }
43972 );
43973
43974 panel.addxtype({
43975         xtype : 'NestedLayoutPanel',
43976         region: 'west',
43977         layout: {
43978            center: { },
43979            west: { }   
43980         },
43981         items : [ ... list of content panels or nested layout panels.. ]
43982    }
43983 );
43984 </code></pre>
43985      * @param {Object} cfg Xtype definition of item to add.
43986      */
43987     addxtype : function(cfg) {
43988         return this.layout.addxtype(cfg);
43989     
43990     }
43991 });/*
43992  * Based on:
43993  * Ext JS Library 1.1.1
43994  * Copyright(c) 2006-2007, Ext JS, LLC.
43995  *
43996  * Originally Released Under LGPL - original licence link has changed is not relivant.
43997  *
43998  * Fork - LGPL
43999  * <script type="text/javascript">
44000  */
44001 /**
44002  * @class Roo.TabPanel
44003  * @extends Roo.util.Observable
44004  * A lightweight tab container.
44005  * <br><br>
44006  * Usage:
44007  * <pre><code>
44008 // basic tabs 1, built from existing content
44009 var tabs = new Roo.TabPanel("tabs1");
44010 tabs.addTab("script", "View Script");
44011 tabs.addTab("markup", "View Markup");
44012 tabs.activate("script");
44013
44014 // more advanced tabs, built from javascript
44015 var jtabs = new Roo.TabPanel("jtabs");
44016 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44017
44018 // set up the UpdateManager
44019 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44020 var updater = tab2.getUpdateManager();
44021 updater.setDefaultUrl("ajax1.htm");
44022 tab2.on('activate', updater.refresh, updater, true);
44023
44024 // Use setUrl for Ajax loading
44025 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44026 tab3.setUrl("ajax2.htm", null, true);
44027
44028 // Disabled tab
44029 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44030 tab4.disable();
44031
44032 jtabs.activate("jtabs-1");
44033  * </code></pre>
44034  * @constructor
44035  * Create a new TabPanel.
44036  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44037  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44038  */
44039 Roo.bootstrap.panel.Tabs = function(config){
44040     /**
44041     * The container element for this TabPanel.
44042     * @type Roo.Element
44043     */
44044     this.el = Roo.get(config.el);
44045     delete config.el;
44046     if(config){
44047         if(typeof config == "boolean"){
44048             this.tabPosition = config ? "bottom" : "top";
44049         }else{
44050             Roo.apply(this, config);
44051         }
44052     }
44053     
44054     if(this.tabPosition == "bottom"){
44055         // if tabs are at the bottom = create the body first.
44056         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44057         this.el.addClass("roo-tabs-bottom");
44058     }
44059     // next create the tabs holders
44060     
44061     if (this.tabPosition == "west"){
44062         
44063         var reg = this.region; // fake it..
44064         while (reg) {
44065             if (!reg.mgr.parent) {
44066                 break;
44067             }
44068             reg = reg.mgr.parent.region;
44069         }
44070         Roo.log("got nest?");
44071         Roo.log(reg);
44072         if (reg.mgr.getRegion('west')) {
44073             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44074             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44075             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44076             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44077             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44078         
44079             
44080         }
44081         
44082         
44083     } else {
44084      
44085         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44086         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44087         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44088         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44089     }
44090     
44091     
44092     if(Roo.isIE){
44093         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44094     }
44095     
44096     // finally - if tabs are at the top, then create the body last..
44097     if(this.tabPosition != "bottom"){
44098         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44099          * @type Roo.Element
44100          */
44101         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44102         this.el.addClass("roo-tabs-top");
44103     }
44104     this.items = [];
44105
44106     this.bodyEl.setStyle("position", "relative");
44107
44108     this.active = null;
44109     this.activateDelegate = this.activate.createDelegate(this);
44110
44111     this.addEvents({
44112         /**
44113          * @event tabchange
44114          * Fires when the active tab changes
44115          * @param {Roo.TabPanel} this
44116          * @param {Roo.TabPanelItem} activePanel The new active tab
44117          */
44118         "tabchange": true,
44119         /**
44120          * @event beforetabchange
44121          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44122          * @param {Roo.TabPanel} this
44123          * @param {Object} e Set cancel to true on this object to cancel the tab change
44124          * @param {Roo.TabPanelItem} tab The tab being changed to
44125          */
44126         "beforetabchange" : true
44127     });
44128
44129     Roo.EventManager.onWindowResize(this.onResize, this);
44130     this.cpad = this.el.getPadding("lr");
44131     this.hiddenCount = 0;
44132
44133
44134     // toolbar on the tabbar support...
44135     if (this.toolbar) {
44136         alert("no toolbar support yet");
44137         this.toolbar  = false;
44138         /*
44139         var tcfg = this.toolbar;
44140         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
44141         this.toolbar = new Roo.Toolbar(tcfg);
44142         if (Roo.isSafari) {
44143             var tbl = tcfg.container.child('table', true);
44144             tbl.setAttribute('width', '100%');
44145         }
44146         */
44147         
44148     }
44149    
44150
44151
44152     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44153 };
44154
44155 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44156     /*
44157      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44158      */
44159     tabPosition : "top",
44160     /*
44161      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44162      */
44163     currentTabWidth : 0,
44164     /*
44165      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44166      */
44167     minTabWidth : 40,
44168     /*
44169      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44170      */
44171     maxTabWidth : 250,
44172     /*
44173      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44174      */
44175     preferredTabWidth : 175,
44176     /*
44177      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44178      */
44179     resizeTabs : false,
44180     /*
44181      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44182      */
44183     monitorResize : true,
44184     /*
44185      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
44186      */
44187     toolbar : false,  // set by caller..
44188     
44189     region : false, /// set by caller
44190     
44191     disableTooltips : true, // not used yet...
44192
44193     /**
44194      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44195      * @param {String} id The id of the div to use <b>or create</b>
44196      * @param {String} text The text for the tab
44197      * @param {String} content (optional) Content to put in the TabPanelItem body
44198      * @param {Boolean} closable (optional) True to create a close icon on the tab
44199      * @return {Roo.TabPanelItem} The created TabPanelItem
44200      */
44201     addTab : function(id, text, content, closable, tpl)
44202     {
44203         var item = new Roo.bootstrap.panel.TabItem({
44204             panel: this,
44205             id : id,
44206             text : text,
44207             closable : closable,
44208             tpl : tpl
44209         });
44210         this.addTabItem(item);
44211         if(content){
44212             item.setContent(content);
44213         }
44214         return item;
44215     },
44216
44217     /**
44218      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44219      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44220      * @return {Roo.TabPanelItem}
44221      */
44222     getTab : function(id){
44223         return this.items[id];
44224     },
44225
44226     /**
44227      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44228      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44229      */
44230     hideTab : function(id){
44231         var t = this.items[id];
44232         if(!t.isHidden()){
44233            t.setHidden(true);
44234            this.hiddenCount++;
44235            this.autoSizeTabs();
44236         }
44237     },
44238
44239     /**
44240      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44241      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44242      */
44243     unhideTab : function(id){
44244         var t = this.items[id];
44245         if(t.isHidden()){
44246            t.setHidden(false);
44247            this.hiddenCount--;
44248            this.autoSizeTabs();
44249         }
44250     },
44251
44252     /**
44253      * Adds an existing {@link Roo.TabPanelItem}.
44254      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44255      */
44256     addTabItem : function(item)
44257     {
44258         this.items[item.id] = item;
44259         this.items.push(item);
44260         this.autoSizeTabs();
44261       //  if(this.resizeTabs){
44262     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44263   //         this.autoSizeTabs();
44264 //        }else{
44265 //            item.autoSize();
44266        // }
44267     },
44268
44269     /**
44270      * Removes a {@link Roo.TabPanelItem}.
44271      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44272      */
44273     removeTab : function(id){
44274         var items = this.items;
44275         var tab = items[id];
44276         if(!tab) { return; }
44277         var index = items.indexOf(tab);
44278         if(this.active == tab && items.length > 1){
44279             var newTab = this.getNextAvailable(index);
44280             if(newTab) {
44281                 newTab.activate();
44282             }
44283         }
44284         this.stripEl.dom.removeChild(tab.pnode.dom);
44285         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44286             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44287         }
44288         items.splice(index, 1);
44289         delete this.items[tab.id];
44290         tab.fireEvent("close", tab);
44291         tab.purgeListeners();
44292         this.autoSizeTabs();
44293     },
44294
44295     getNextAvailable : function(start){
44296         var items = this.items;
44297         var index = start;
44298         // look for a next tab that will slide over to
44299         // replace the one being removed
44300         while(index < items.length){
44301             var item = items[++index];
44302             if(item && !item.isHidden()){
44303                 return item;
44304             }
44305         }
44306         // if one isn't found select the previous tab (on the left)
44307         index = start;
44308         while(index >= 0){
44309             var item = items[--index];
44310             if(item && !item.isHidden()){
44311                 return item;
44312             }
44313         }
44314         return null;
44315     },
44316
44317     /**
44318      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44319      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44320      */
44321     disableTab : function(id){
44322         var tab = this.items[id];
44323         if(tab && this.active != tab){
44324             tab.disable();
44325         }
44326     },
44327
44328     /**
44329      * Enables a {@link Roo.TabPanelItem} that is disabled.
44330      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44331      */
44332     enableTab : function(id){
44333         var tab = this.items[id];
44334         tab.enable();
44335     },
44336
44337     /**
44338      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44339      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44340      * @return {Roo.TabPanelItem} The TabPanelItem.
44341      */
44342     activate : function(id)
44343     {
44344         //Roo.log('activite:'  + id);
44345         
44346         var tab = this.items[id];
44347         if(!tab){
44348             return null;
44349         }
44350         if(tab == this.active || tab.disabled){
44351             return tab;
44352         }
44353         var e = {};
44354         this.fireEvent("beforetabchange", this, e, tab);
44355         if(e.cancel !== true && !tab.disabled){
44356             if(this.active){
44357                 this.active.hide();
44358             }
44359             this.active = this.items[id];
44360             this.active.show();
44361             this.fireEvent("tabchange", this, this.active);
44362         }
44363         return tab;
44364     },
44365
44366     /**
44367      * Gets the active {@link Roo.TabPanelItem}.
44368      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44369      */
44370     getActiveTab : function(){
44371         return this.active;
44372     },
44373
44374     /**
44375      * Updates the tab body element to fit the height of the container element
44376      * for overflow scrolling
44377      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44378      */
44379     syncHeight : function(targetHeight){
44380         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44381         var bm = this.bodyEl.getMargins();
44382         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44383         this.bodyEl.setHeight(newHeight);
44384         return newHeight;
44385     },
44386
44387     onResize : function(){
44388         if(this.monitorResize){
44389             this.autoSizeTabs();
44390         }
44391     },
44392
44393     /**
44394      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44395      */
44396     beginUpdate : function(){
44397         this.updating = true;
44398     },
44399
44400     /**
44401      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44402      */
44403     endUpdate : function(){
44404         this.updating = false;
44405         this.autoSizeTabs();
44406     },
44407
44408     /**
44409      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44410      */
44411     autoSizeTabs : function()
44412     {
44413         var count = this.items.length;
44414         var vcount = count - this.hiddenCount;
44415         
44416         if (vcount < 2) {
44417             this.stripEl.hide();
44418         } else {
44419             this.stripEl.show();
44420         }
44421         
44422         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44423             return;
44424         }
44425         
44426         
44427         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44428         var availWidth = Math.floor(w / vcount);
44429         var b = this.stripBody;
44430         if(b.getWidth() > w){
44431             var tabs = this.items;
44432             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44433             if(availWidth < this.minTabWidth){
44434                 /*if(!this.sleft){    // incomplete scrolling code
44435                     this.createScrollButtons();
44436                 }
44437                 this.showScroll();
44438                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44439             }
44440         }else{
44441             if(this.currentTabWidth < this.preferredTabWidth){
44442                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44443             }
44444         }
44445     },
44446
44447     /**
44448      * Returns the number of tabs in this TabPanel.
44449      * @return {Number}
44450      */
44451      getCount : function(){
44452          return this.items.length;
44453      },
44454
44455     /**
44456      * Resizes all the tabs to the passed width
44457      * @param {Number} The new width
44458      */
44459     setTabWidth : function(width){
44460         this.currentTabWidth = width;
44461         for(var i = 0, len = this.items.length; i < len; i++) {
44462                 if(!this.items[i].isHidden()) {
44463                 this.items[i].setWidth(width);
44464             }
44465         }
44466     },
44467
44468     /**
44469      * Destroys this TabPanel
44470      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44471      */
44472     destroy : function(removeEl){
44473         Roo.EventManager.removeResizeListener(this.onResize, this);
44474         for(var i = 0, len = this.items.length; i < len; i++){
44475             this.items[i].purgeListeners();
44476         }
44477         if(removeEl === true){
44478             this.el.update("");
44479             this.el.remove();
44480         }
44481     },
44482     
44483     createStrip : function(container)
44484     {
44485         var strip = document.createElement("nav");
44486         strip.className = Roo.bootstrap.version == 4 ?
44487             "navbar-light bg-light" : 
44488             "navbar navbar-default"; //"x-tabs-wrap";
44489         container.appendChild(strip);
44490         return strip;
44491     },
44492     
44493     createStripList : function(strip)
44494     {
44495         // div wrapper for retard IE
44496         // returns the "tr" element.
44497         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44498         //'<div class="x-tabs-strip-wrap">'+
44499           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44500           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44501         return strip.firstChild; //.firstChild.firstChild.firstChild;
44502     },
44503     createBody : function(container)
44504     {
44505         var body = document.createElement("div");
44506         Roo.id(body, "tab-body");
44507         //Roo.fly(body).addClass("x-tabs-body");
44508         Roo.fly(body).addClass("tab-content");
44509         container.appendChild(body);
44510         return body;
44511     },
44512     createItemBody :function(bodyEl, id){
44513         var body = Roo.getDom(id);
44514         if(!body){
44515             body = document.createElement("div");
44516             body.id = id;
44517         }
44518         //Roo.fly(body).addClass("x-tabs-item-body");
44519         Roo.fly(body).addClass("tab-pane");
44520          bodyEl.insertBefore(body, bodyEl.firstChild);
44521         return body;
44522     },
44523     /** @private */
44524     createStripElements :  function(stripEl, text, closable, tpl)
44525     {
44526         var td = document.createElement("li"); // was td..
44527         td.className = 'nav-item';
44528         
44529         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44530         
44531         
44532         stripEl.appendChild(td);
44533         /*if(closable){
44534             td.className = "x-tabs-closable";
44535             if(!this.closeTpl){
44536                 this.closeTpl = new Roo.Template(
44537                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44538                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44539                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44540                 );
44541             }
44542             var el = this.closeTpl.overwrite(td, {"text": text});
44543             var close = el.getElementsByTagName("div")[0];
44544             var inner = el.getElementsByTagName("em")[0];
44545             return {"el": el, "close": close, "inner": inner};
44546         } else {
44547         */
44548         // not sure what this is..
44549 //            if(!this.tabTpl){
44550                 //this.tabTpl = new Roo.Template(
44551                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44552                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44553                 //);
44554 //                this.tabTpl = new Roo.Template(
44555 //                   '<a href="#">' +
44556 //                   '<span unselectable="on"' +
44557 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44558 //                            ' >{text}</span></a>'
44559 //                );
44560 //                
44561 //            }
44562
44563
44564             var template = tpl || this.tabTpl || false;
44565             
44566             if(!template){
44567                 template =  new Roo.Template(
44568                         Roo.bootstrap.version == 4 ? 
44569                             (
44570                                 '<a class="nav-link" href="#" unselectable="on"' +
44571                                      (this.disableTooltips ? '' : ' title="{text}"') +
44572                                      ' >{text}</a>'
44573                             ) : (
44574                                 '<a class="nav-link" href="#">' +
44575                                 '<span unselectable="on"' +
44576                                          (this.disableTooltips ? '' : ' title="{text}"') +
44577                                     ' >{text}</span></a>'
44578                             )
44579                 );
44580             }
44581             
44582             switch (typeof(template)) {
44583                 case 'object' :
44584                     break;
44585                 case 'string' :
44586                     template = new Roo.Template(template);
44587                     break;
44588                 default :
44589                     break;
44590             }
44591             
44592             var el = template.overwrite(td, {"text": text});
44593             
44594             var inner = el.getElementsByTagName("span")[0];
44595             
44596             return {"el": el, "inner": inner};
44597             
44598     }
44599         
44600     
44601 });
44602
44603 /**
44604  * @class Roo.TabPanelItem
44605  * @extends Roo.util.Observable
44606  * Represents an individual item (tab plus body) in a TabPanel.
44607  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44608  * @param {String} id The id of this TabPanelItem
44609  * @param {String} text The text for the tab of this TabPanelItem
44610  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44611  */
44612 Roo.bootstrap.panel.TabItem = function(config){
44613     /**
44614      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44615      * @type Roo.TabPanel
44616      */
44617     this.tabPanel = config.panel;
44618     /**
44619      * The id for this TabPanelItem
44620      * @type String
44621      */
44622     this.id = config.id;
44623     /** @private */
44624     this.disabled = false;
44625     /** @private */
44626     this.text = config.text;
44627     /** @private */
44628     this.loaded = false;
44629     this.closable = config.closable;
44630
44631     /**
44632      * The body element for this TabPanelItem.
44633      * @type Roo.Element
44634      */
44635     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44636     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44637     this.bodyEl.setStyle("display", "block");
44638     this.bodyEl.setStyle("zoom", "1");
44639     //this.hideAction();
44640
44641     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44642     /** @private */
44643     this.el = Roo.get(els.el);
44644     this.inner = Roo.get(els.inner, true);
44645      this.textEl = Roo.bootstrap.version == 4 ?
44646         this.el : Roo.get(this.el.dom.firstChild, true);
44647
44648     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44649     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44650
44651     
44652 //    this.el.on("mousedown", this.onTabMouseDown, this);
44653     this.el.on("click", this.onTabClick, this);
44654     /** @private */
44655     if(config.closable){
44656         var c = Roo.get(els.close, true);
44657         c.dom.title = this.closeText;
44658         c.addClassOnOver("close-over");
44659         c.on("click", this.closeClick, this);
44660      }
44661
44662     this.addEvents({
44663          /**
44664          * @event activate
44665          * Fires when this tab becomes the active tab.
44666          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44667          * @param {Roo.TabPanelItem} this
44668          */
44669         "activate": true,
44670         /**
44671          * @event beforeclose
44672          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44673          * @param {Roo.TabPanelItem} this
44674          * @param {Object} e Set cancel to true on this object to cancel the close.
44675          */
44676         "beforeclose": true,
44677         /**
44678          * @event close
44679          * Fires when this tab is closed.
44680          * @param {Roo.TabPanelItem} this
44681          */
44682          "close": true,
44683         /**
44684          * @event deactivate
44685          * Fires when this tab is no longer the active tab.
44686          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44687          * @param {Roo.TabPanelItem} this
44688          */
44689          "deactivate" : true
44690     });
44691     this.hidden = false;
44692
44693     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44694 };
44695
44696 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44697            {
44698     purgeListeners : function(){
44699        Roo.util.Observable.prototype.purgeListeners.call(this);
44700        this.el.removeAllListeners();
44701     },
44702     /**
44703      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44704      */
44705     show : function(){
44706         this.status_node.addClass("active");
44707         this.showAction();
44708         if(Roo.isOpera){
44709             this.tabPanel.stripWrap.repaint();
44710         }
44711         this.fireEvent("activate", this.tabPanel, this);
44712     },
44713
44714     /**
44715      * Returns true if this tab is the active tab.
44716      * @return {Boolean}
44717      */
44718     isActive : function(){
44719         return this.tabPanel.getActiveTab() == this;
44720     },
44721
44722     /**
44723      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44724      */
44725     hide : function(){
44726         this.status_node.removeClass("active");
44727         this.hideAction();
44728         this.fireEvent("deactivate", this.tabPanel, this);
44729     },
44730
44731     hideAction : function(){
44732         this.bodyEl.hide();
44733         this.bodyEl.setStyle("position", "absolute");
44734         this.bodyEl.setLeft("-20000px");
44735         this.bodyEl.setTop("-20000px");
44736     },
44737
44738     showAction : function(){
44739         this.bodyEl.setStyle("position", "relative");
44740         this.bodyEl.setTop("");
44741         this.bodyEl.setLeft("");
44742         this.bodyEl.show();
44743     },
44744
44745     /**
44746      * Set the tooltip for the tab.
44747      * @param {String} tooltip The tab's tooltip
44748      */
44749     setTooltip : function(text){
44750         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44751             this.textEl.dom.qtip = text;
44752             this.textEl.dom.removeAttribute('title');
44753         }else{
44754             this.textEl.dom.title = text;
44755         }
44756     },
44757
44758     onTabClick : function(e){
44759         e.preventDefault();
44760         this.tabPanel.activate(this.id);
44761     },
44762
44763     onTabMouseDown : function(e){
44764         e.preventDefault();
44765         this.tabPanel.activate(this.id);
44766     },
44767 /*
44768     getWidth : function(){
44769         return this.inner.getWidth();
44770     },
44771
44772     setWidth : function(width){
44773         var iwidth = width - this.linode.getPadding("lr");
44774         this.inner.setWidth(iwidth);
44775         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44776         this.linode.setWidth(width);
44777     },
44778 */
44779     /**
44780      * Show or hide the tab
44781      * @param {Boolean} hidden True to hide or false to show.
44782      */
44783     setHidden : function(hidden){
44784         this.hidden = hidden;
44785         this.linode.setStyle("display", hidden ? "none" : "");
44786     },
44787
44788     /**
44789      * Returns true if this tab is "hidden"
44790      * @return {Boolean}
44791      */
44792     isHidden : function(){
44793         return this.hidden;
44794     },
44795
44796     /**
44797      * Returns the text for this tab
44798      * @return {String}
44799      */
44800     getText : function(){
44801         return this.text;
44802     },
44803     /*
44804     autoSize : function(){
44805         //this.el.beginMeasure();
44806         this.textEl.setWidth(1);
44807         /*
44808          *  #2804 [new] Tabs in Roojs
44809          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44810          */
44811         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44812         //this.el.endMeasure();
44813     //},
44814
44815     /**
44816      * Sets the text for the tab (Note: this also sets the tooltip text)
44817      * @param {String} text The tab's text and tooltip
44818      */
44819     setText : function(text){
44820         this.text = text;
44821         this.textEl.update(text);
44822         this.setTooltip(text);
44823         //if(!this.tabPanel.resizeTabs){
44824         //    this.autoSize();
44825         //}
44826     },
44827     /**
44828      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44829      */
44830     activate : function(){
44831         this.tabPanel.activate(this.id);
44832     },
44833
44834     /**
44835      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44836      */
44837     disable : function(){
44838         if(this.tabPanel.active != this){
44839             this.disabled = true;
44840             this.status_node.addClass("disabled");
44841         }
44842     },
44843
44844     /**
44845      * Enables this TabPanelItem if it was previously disabled.
44846      */
44847     enable : function(){
44848         this.disabled = false;
44849         this.status_node.removeClass("disabled");
44850     },
44851
44852     /**
44853      * Sets the content for this TabPanelItem.
44854      * @param {String} content The content
44855      * @param {Boolean} loadScripts true to look for and load scripts
44856      */
44857     setContent : function(content, loadScripts){
44858         this.bodyEl.update(content, loadScripts);
44859     },
44860
44861     /**
44862      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44863      * @return {Roo.UpdateManager} The UpdateManager
44864      */
44865     getUpdateManager : function(){
44866         return this.bodyEl.getUpdateManager();
44867     },
44868
44869     /**
44870      * Set a URL to be used to load the content for this TabPanelItem.
44871      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44872      * @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)
44873      * @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)
44874      * @return {Roo.UpdateManager} The UpdateManager
44875      */
44876     setUrl : function(url, params, loadOnce){
44877         if(this.refreshDelegate){
44878             this.un('activate', this.refreshDelegate);
44879         }
44880         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44881         this.on("activate", this.refreshDelegate);
44882         return this.bodyEl.getUpdateManager();
44883     },
44884
44885     /** @private */
44886     _handleRefresh : function(url, params, loadOnce){
44887         if(!loadOnce || !this.loaded){
44888             var updater = this.bodyEl.getUpdateManager();
44889             updater.update(url, params, this._setLoaded.createDelegate(this));
44890         }
44891     },
44892
44893     /**
44894      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44895      *   Will fail silently if the setUrl method has not been called.
44896      *   This does not activate the panel, just updates its content.
44897      */
44898     refresh : function(){
44899         if(this.refreshDelegate){
44900            this.loaded = false;
44901            this.refreshDelegate();
44902         }
44903     },
44904
44905     /** @private */
44906     _setLoaded : function(){
44907         this.loaded = true;
44908     },
44909
44910     /** @private */
44911     closeClick : function(e){
44912         var o = {};
44913         e.stopEvent();
44914         this.fireEvent("beforeclose", this, o);
44915         if(o.cancel !== true){
44916             this.tabPanel.removeTab(this.id);
44917         }
44918     },
44919     /**
44920      * The text displayed in the tooltip for the close icon.
44921      * @type String
44922      */
44923     closeText : "Close this tab"
44924 });
44925 /**
44926 *    This script refer to:
44927 *    Title: International Telephone Input
44928 *    Author: Jack O'Connor
44929 *    Code version:  v12.1.12
44930 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44931 **/
44932
44933 Roo.bootstrap.form.PhoneInputData = function() {
44934     var d = [
44935       [
44936         "Afghanistan (‫افغانستان‬‎)",
44937         "af",
44938         "93"
44939       ],
44940       [
44941         "Albania (Shqipëri)",
44942         "al",
44943         "355"
44944       ],
44945       [
44946         "Algeria (‫الجزائر‬‎)",
44947         "dz",
44948         "213"
44949       ],
44950       [
44951         "American Samoa",
44952         "as",
44953         "1684"
44954       ],
44955       [
44956         "Andorra",
44957         "ad",
44958         "376"
44959       ],
44960       [
44961         "Angola",
44962         "ao",
44963         "244"
44964       ],
44965       [
44966         "Anguilla",
44967         "ai",
44968         "1264"
44969       ],
44970       [
44971         "Antigua and Barbuda",
44972         "ag",
44973         "1268"
44974       ],
44975       [
44976         "Argentina",
44977         "ar",
44978         "54"
44979       ],
44980       [
44981         "Armenia (Հայաստան)",
44982         "am",
44983         "374"
44984       ],
44985       [
44986         "Aruba",
44987         "aw",
44988         "297"
44989       ],
44990       [
44991         "Australia",
44992         "au",
44993         "61",
44994         0
44995       ],
44996       [
44997         "Austria (Österreich)",
44998         "at",
44999         "43"
45000       ],
45001       [
45002         "Azerbaijan (Azərbaycan)",
45003         "az",
45004         "994"
45005       ],
45006       [
45007         "Bahamas",
45008         "bs",
45009         "1242"
45010       ],
45011       [
45012         "Bahrain (‫البحرين‬‎)",
45013         "bh",
45014         "973"
45015       ],
45016       [
45017         "Bangladesh (বাংলাদেশ)",
45018         "bd",
45019         "880"
45020       ],
45021       [
45022         "Barbados",
45023         "bb",
45024         "1246"
45025       ],
45026       [
45027         "Belarus (Беларусь)",
45028         "by",
45029         "375"
45030       ],
45031       [
45032         "Belgium (België)",
45033         "be",
45034         "32"
45035       ],
45036       [
45037         "Belize",
45038         "bz",
45039         "501"
45040       ],
45041       [
45042         "Benin (Bénin)",
45043         "bj",
45044         "229"
45045       ],
45046       [
45047         "Bermuda",
45048         "bm",
45049         "1441"
45050       ],
45051       [
45052         "Bhutan (འབྲུག)",
45053         "bt",
45054         "975"
45055       ],
45056       [
45057         "Bolivia",
45058         "bo",
45059         "591"
45060       ],
45061       [
45062         "Bosnia and Herzegovina (Босна и Херцеговина)",
45063         "ba",
45064         "387"
45065       ],
45066       [
45067         "Botswana",
45068         "bw",
45069         "267"
45070       ],
45071       [
45072         "Brazil (Brasil)",
45073         "br",
45074         "55"
45075       ],
45076       [
45077         "British Indian Ocean Territory",
45078         "io",
45079         "246"
45080       ],
45081       [
45082         "British Virgin Islands",
45083         "vg",
45084         "1284"
45085       ],
45086       [
45087         "Brunei",
45088         "bn",
45089         "673"
45090       ],
45091       [
45092         "Bulgaria (България)",
45093         "bg",
45094         "359"
45095       ],
45096       [
45097         "Burkina Faso",
45098         "bf",
45099         "226"
45100       ],
45101       [
45102         "Burundi (Uburundi)",
45103         "bi",
45104         "257"
45105       ],
45106       [
45107         "Cambodia (កម្ពុជា)",
45108         "kh",
45109         "855"
45110       ],
45111       [
45112         "Cameroon (Cameroun)",
45113         "cm",
45114         "237"
45115       ],
45116       [
45117         "Canada",
45118         "ca",
45119         "1",
45120         1,
45121         ["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"]
45122       ],
45123       [
45124         "Cape Verde (Kabu Verdi)",
45125         "cv",
45126         "238"
45127       ],
45128       [
45129         "Caribbean Netherlands",
45130         "bq",
45131         "599",
45132         1
45133       ],
45134       [
45135         "Cayman Islands",
45136         "ky",
45137         "1345"
45138       ],
45139       [
45140         "Central African Republic (République centrafricaine)",
45141         "cf",
45142         "236"
45143       ],
45144       [
45145         "Chad (Tchad)",
45146         "td",
45147         "235"
45148       ],
45149       [
45150         "Chile",
45151         "cl",
45152         "56"
45153       ],
45154       [
45155         "China (中国)",
45156         "cn",
45157         "86"
45158       ],
45159       [
45160         "Christmas Island",
45161         "cx",
45162         "61",
45163         2
45164       ],
45165       [
45166         "Cocos (Keeling) Islands",
45167         "cc",
45168         "61",
45169         1
45170       ],
45171       [
45172         "Colombia",
45173         "co",
45174         "57"
45175       ],
45176       [
45177         "Comoros (‫جزر القمر‬‎)",
45178         "km",
45179         "269"
45180       ],
45181       [
45182         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45183         "cd",
45184         "243"
45185       ],
45186       [
45187         "Congo (Republic) (Congo-Brazzaville)",
45188         "cg",
45189         "242"
45190       ],
45191       [
45192         "Cook Islands",
45193         "ck",
45194         "682"
45195       ],
45196       [
45197         "Costa Rica",
45198         "cr",
45199         "506"
45200       ],
45201       [
45202         "Côte d’Ivoire",
45203         "ci",
45204         "225"
45205       ],
45206       [
45207         "Croatia (Hrvatska)",
45208         "hr",
45209         "385"
45210       ],
45211       [
45212         "Cuba",
45213         "cu",
45214         "53"
45215       ],
45216       [
45217         "Curaçao",
45218         "cw",
45219         "599",
45220         0
45221       ],
45222       [
45223         "Cyprus (Κύπρος)",
45224         "cy",
45225         "357"
45226       ],
45227       [
45228         "Czech Republic (Česká republika)",
45229         "cz",
45230         "420"
45231       ],
45232       [
45233         "Denmark (Danmark)",
45234         "dk",
45235         "45"
45236       ],
45237       [
45238         "Djibouti",
45239         "dj",
45240         "253"
45241       ],
45242       [
45243         "Dominica",
45244         "dm",
45245         "1767"
45246       ],
45247       [
45248         "Dominican Republic (República Dominicana)",
45249         "do",
45250         "1",
45251         2,
45252         ["809", "829", "849"]
45253       ],
45254       [
45255         "Ecuador",
45256         "ec",
45257         "593"
45258       ],
45259       [
45260         "Egypt (‫مصر‬‎)",
45261         "eg",
45262         "20"
45263       ],
45264       [
45265         "El Salvador",
45266         "sv",
45267         "503"
45268       ],
45269       [
45270         "Equatorial Guinea (Guinea Ecuatorial)",
45271         "gq",
45272         "240"
45273       ],
45274       [
45275         "Eritrea",
45276         "er",
45277         "291"
45278       ],
45279       [
45280         "Estonia (Eesti)",
45281         "ee",
45282         "372"
45283       ],
45284       [
45285         "Ethiopia",
45286         "et",
45287         "251"
45288       ],
45289       [
45290         "Falkland Islands (Islas Malvinas)",
45291         "fk",
45292         "500"
45293       ],
45294       [
45295         "Faroe Islands (Føroyar)",
45296         "fo",
45297         "298"
45298       ],
45299       [
45300         "Fiji",
45301         "fj",
45302         "679"
45303       ],
45304       [
45305         "Finland (Suomi)",
45306         "fi",
45307         "358",
45308         0
45309       ],
45310       [
45311         "France",
45312         "fr",
45313         "33"
45314       ],
45315       [
45316         "French Guiana (Guyane française)",
45317         "gf",
45318         "594"
45319       ],
45320       [
45321         "French Polynesia (Polynésie française)",
45322         "pf",
45323         "689"
45324       ],
45325       [
45326         "Gabon",
45327         "ga",
45328         "241"
45329       ],
45330       [
45331         "Gambia",
45332         "gm",
45333         "220"
45334       ],
45335       [
45336         "Georgia (საქართველო)",
45337         "ge",
45338         "995"
45339       ],
45340       [
45341         "Germany (Deutschland)",
45342         "de",
45343         "49"
45344       ],
45345       [
45346         "Ghana (Gaana)",
45347         "gh",
45348         "233"
45349       ],
45350       [
45351         "Gibraltar",
45352         "gi",
45353         "350"
45354       ],
45355       [
45356         "Greece (Ελλάδα)",
45357         "gr",
45358         "30"
45359       ],
45360       [
45361         "Greenland (Kalaallit Nunaat)",
45362         "gl",
45363         "299"
45364       ],
45365       [
45366         "Grenada",
45367         "gd",
45368         "1473"
45369       ],
45370       [
45371         "Guadeloupe",
45372         "gp",
45373         "590",
45374         0
45375       ],
45376       [
45377         "Guam",
45378         "gu",
45379         "1671"
45380       ],
45381       [
45382         "Guatemala",
45383         "gt",
45384         "502"
45385       ],
45386       [
45387         "Guernsey",
45388         "gg",
45389         "44",
45390         1
45391       ],
45392       [
45393         "Guinea (Guinée)",
45394         "gn",
45395         "224"
45396       ],
45397       [
45398         "Guinea-Bissau (Guiné Bissau)",
45399         "gw",
45400         "245"
45401       ],
45402       [
45403         "Guyana",
45404         "gy",
45405         "592"
45406       ],
45407       [
45408         "Haiti",
45409         "ht",
45410         "509"
45411       ],
45412       [
45413         "Honduras",
45414         "hn",
45415         "504"
45416       ],
45417       [
45418         "Hong Kong (香港)",
45419         "hk",
45420         "852"
45421       ],
45422       [
45423         "Hungary (Magyarország)",
45424         "hu",
45425         "36"
45426       ],
45427       [
45428         "Iceland (Ísland)",
45429         "is",
45430         "354"
45431       ],
45432       [
45433         "India (भारत)",
45434         "in",
45435         "91"
45436       ],
45437       [
45438         "Indonesia",
45439         "id",
45440         "62"
45441       ],
45442       [
45443         "Iran (‫ایران‬‎)",
45444         "ir",
45445         "98"
45446       ],
45447       [
45448         "Iraq (‫العراق‬‎)",
45449         "iq",
45450         "964"
45451       ],
45452       [
45453         "Ireland",
45454         "ie",
45455         "353"
45456       ],
45457       [
45458         "Isle of Man",
45459         "im",
45460         "44",
45461         2
45462       ],
45463       [
45464         "Israel (‫ישראל‬‎)",
45465         "il",
45466         "972"
45467       ],
45468       [
45469         "Italy (Italia)",
45470         "it",
45471         "39",
45472         0
45473       ],
45474       [
45475         "Jamaica",
45476         "jm",
45477         "1876"
45478       ],
45479       [
45480         "Japan (日本)",
45481         "jp",
45482         "81"
45483       ],
45484       [
45485         "Jersey",
45486         "je",
45487         "44",
45488         3
45489       ],
45490       [
45491         "Jordan (‫الأردن‬‎)",
45492         "jo",
45493         "962"
45494       ],
45495       [
45496         "Kazakhstan (Казахстан)",
45497         "kz",
45498         "7",
45499         1
45500       ],
45501       [
45502         "Kenya",
45503         "ke",
45504         "254"
45505       ],
45506       [
45507         "Kiribati",
45508         "ki",
45509         "686"
45510       ],
45511       [
45512         "Kosovo",
45513         "xk",
45514         "383"
45515       ],
45516       [
45517         "Kuwait (‫الكويت‬‎)",
45518         "kw",
45519         "965"
45520       ],
45521       [
45522         "Kyrgyzstan (Кыргызстан)",
45523         "kg",
45524         "996"
45525       ],
45526       [
45527         "Laos (ລາວ)",
45528         "la",
45529         "856"
45530       ],
45531       [
45532         "Latvia (Latvija)",
45533         "lv",
45534         "371"
45535       ],
45536       [
45537         "Lebanon (‫لبنان‬‎)",
45538         "lb",
45539         "961"
45540       ],
45541       [
45542         "Lesotho",
45543         "ls",
45544         "266"
45545       ],
45546       [
45547         "Liberia",
45548         "lr",
45549         "231"
45550       ],
45551       [
45552         "Libya (‫ليبيا‬‎)",
45553         "ly",
45554         "218"
45555       ],
45556       [
45557         "Liechtenstein",
45558         "li",
45559         "423"
45560       ],
45561       [
45562         "Lithuania (Lietuva)",
45563         "lt",
45564         "370"
45565       ],
45566       [
45567         "Luxembourg",
45568         "lu",
45569         "352"
45570       ],
45571       [
45572         "Macau (澳門)",
45573         "mo",
45574         "853"
45575       ],
45576       [
45577         "Macedonia (FYROM) (Македонија)",
45578         "mk",
45579         "389"
45580       ],
45581       [
45582         "Madagascar (Madagasikara)",
45583         "mg",
45584         "261"
45585       ],
45586       [
45587         "Malawi",
45588         "mw",
45589         "265"
45590       ],
45591       [
45592         "Malaysia",
45593         "my",
45594         "60"
45595       ],
45596       [
45597         "Maldives",
45598         "mv",
45599         "960"
45600       ],
45601       [
45602         "Mali",
45603         "ml",
45604         "223"
45605       ],
45606       [
45607         "Malta",
45608         "mt",
45609         "356"
45610       ],
45611       [
45612         "Marshall Islands",
45613         "mh",
45614         "692"
45615       ],
45616       [
45617         "Martinique",
45618         "mq",
45619         "596"
45620       ],
45621       [
45622         "Mauritania (‫موريتانيا‬‎)",
45623         "mr",
45624         "222"
45625       ],
45626       [
45627         "Mauritius (Moris)",
45628         "mu",
45629         "230"
45630       ],
45631       [
45632         "Mayotte",
45633         "yt",
45634         "262",
45635         1
45636       ],
45637       [
45638         "Mexico (México)",
45639         "mx",
45640         "52"
45641       ],
45642       [
45643         "Micronesia",
45644         "fm",
45645         "691"
45646       ],
45647       [
45648         "Moldova (Republica Moldova)",
45649         "md",
45650         "373"
45651       ],
45652       [
45653         "Monaco",
45654         "mc",
45655         "377"
45656       ],
45657       [
45658         "Mongolia (Монгол)",
45659         "mn",
45660         "976"
45661       ],
45662       [
45663         "Montenegro (Crna Gora)",
45664         "me",
45665         "382"
45666       ],
45667       [
45668         "Montserrat",
45669         "ms",
45670         "1664"
45671       ],
45672       [
45673         "Morocco (‫المغرب‬‎)",
45674         "ma",
45675         "212",
45676         0
45677       ],
45678       [
45679         "Mozambique (Moçambique)",
45680         "mz",
45681         "258"
45682       ],
45683       [
45684         "Myanmar (Burma) (မြန်မာ)",
45685         "mm",
45686         "95"
45687       ],
45688       [
45689         "Namibia (Namibië)",
45690         "na",
45691         "264"
45692       ],
45693       [
45694         "Nauru",
45695         "nr",
45696         "674"
45697       ],
45698       [
45699         "Nepal (नेपाल)",
45700         "np",
45701         "977"
45702       ],
45703       [
45704         "Netherlands (Nederland)",
45705         "nl",
45706         "31"
45707       ],
45708       [
45709         "New Caledonia (Nouvelle-Calédonie)",
45710         "nc",
45711         "687"
45712       ],
45713       [
45714         "New Zealand",
45715         "nz",
45716         "64"
45717       ],
45718       [
45719         "Nicaragua",
45720         "ni",
45721         "505"
45722       ],
45723       [
45724         "Niger (Nijar)",
45725         "ne",
45726         "227"
45727       ],
45728       [
45729         "Nigeria",
45730         "ng",
45731         "234"
45732       ],
45733       [
45734         "Niue",
45735         "nu",
45736         "683"
45737       ],
45738       [
45739         "Norfolk Island",
45740         "nf",
45741         "672"
45742       ],
45743       [
45744         "North Korea (조선 민주주의 인민 공화국)",
45745         "kp",
45746         "850"
45747       ],
45748       [
45749         "Northern Mariana Islands",
45750         "mp",
45751         "1670"
45752       ],
45753       [
45754         "Norway (Norge)",
45755         "no",
45756         "47",
45757         0
45758       ],
45759       [
45760         "Oman (‫عُمان‬‎)",
45761         "om",
45762         "968"
45763       ],
45764       [
45765         "Pakistan (‫پاکستان‬‎)",
45766         "pk",
45767         "92"
45768       ],
45769       [
45770         "Palau",
45771         "pw",
45772         "680"
45773       ],
45774       [
45775         "Palestine (‫فلسطين‬‎)",
45776         "ps",
45777         "970"
45778       ],
45779       [
45780         "Panama (Panamá)",
45781         "pa",
45782         "507"
45783       ],
45784       [
45785         "Papua New Guinea",
45786         "pg",
45787         "675"
45788       ],
45789       [
45790         "Paraguay",
45791         "py",
45792         "595"
45793       ],
45794       [
45795         "Peru (Perú)",
45796         "pe",
45797         "51"
45798       ],
45799       [
45800         "Philippines",
45801         "ph",
45802         "63"
45803       ],
45804       [
45805         "Poland (Polska)",
45806         "pl",
45807         "48"
45808       ],
45809       [
45810         "Portugal",
45811         "pt",
45812         "351"
45813       ],
45814       [
45815         "Puerto Rico",
45816         "pr",
45817         "1",
45818         3,
45819         ["787", "939"]
45820       ],
45821       [
45822         "Qatar (‫قطر‬‎)",
45823         "qa",
45824         "974"
45825       ],
45826       [
45827         "Réunion (La Réunion)",
45828         "re",
45829         "262",
45830         0
45831       ],
45832       [
45833         "Romania (România)",
45834         "ro",
45835         "40"
45836       ],
45837       [
45838         "Russia (Россия)",
45839         "ru",
45840         "7",
45841         0
45842       ],
45843       [
45844         "Rwanda",
45845         "rw",
45846         "250"
45847       ],
45848       [
45849         "Saint Barthélemy",
45850         "bl",
45851         "590",
45852         1
45853       ],
45854       [
45855         "Saint Helena",
45856         "sh",
45857         "290"
45858       ],
45859       [
45860         "Saint Kitts and Nevis",
45861         "kn",
45862         "1869"
45863       ],
45864       [
45865         "Saint Lucia",
45866         "lc",
45867         "1758"
45868       ],
45869       [
45870         "Saint Martin (Saint-Martin (partie française))",
45871         "mf",
45872         "590",
45873         2
45874       ],
45875       [
45876         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45877         "pm",
45878         "508"
45879       ],
45880       [
45881         "Saint Vincent and the Grenadines",
45882         "vc",
45883         "1784"
45884       ],
45885       [
45886         "Samoa",
45887         "ws",
45888         "685"
45889       ],
45890       [
45891         "San Marino",
45892         "sm",
45893         "378"
45894       ],
45895       [
45896         "São Tomé and Príncipe (São Tomé e Príncipe)",
45897         "st",
45898         "239"
45899       ],
45900       [
45901         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45902         "sa",
45903         "966"
45904       ],
45905       [
45906         "Senegal (Sénégal)",
45907         "sn",
45908         "221"
45909       ],
45910       [
45911         "Serbia (Србија)",
45912         "rs",
45913         "381"
45914       ],
45915       [
45916         "Seychelles",
45917         "sc",
45918         "248"
45919       ],
45920       [
45921         "Sierra Leone",
45922         "sl",
45923         "232"
45924       ],
45925       [
45926         "Singapore",
45927         "sg",
45928         "65"
45929       ],
45930       [
45931         "Sint Maarten",
45932         "sx",
45933         "1721"
45934       ],
45935       [
45936         "Slovakia (Slovensko)",
45937         "sk",
45938         "421"
45939       ],
45940       [
45941         "Slovenia (Slovenija)",
45942         "si",
45943         "386"
45944       ],
45945       [
45946         "Solomon Islands",
45947         "sb",
45948         "677"
45949       ],
45950       [
45951         "Somalia (Soomaaliya)",
45952         "so",
45953         "252"
45954       ],
45955       [
45956         "South Africa",
45957         "za",
45958         "27"
45959       ],
45960       [
45961         "South Korea (대한민국)",
45962         "kr",
45963         "82"
45964       ],
45965       [
45966         "South Sudan (‫جنوب السودان‬‎)",
45967         "ss",
45968         "211"
45969       ],
45970       [
45971         "Spain (España)",
45972         "es",
45973         "34"
45974       ],
45975       [
45976         "Sri Lanka (ශ්‍රී ලංකාව)",
45977         "lk",
45978         "94"
45979       ],
45980       [
45981         "Sudan (‫السودان‬‎)",
45982         "sd",
45983         "249"
45984       ],
45985       [
45986         "Suriname",
45987         "sr",
45988         "597"
45989       ],
45990       [
45991         "Svalbard and Jan Mayen",
45992         "sj",
45993         "47",
45994         1
45995       ],
45996       [
45997         "Swaziland",
45998         "sz",
45999         "268"
46000       ],
46001       [
46002         "Sweden (Sverige)",
46003         "se",
46004         "46"
46005       ],
46006       [
46007         "Switzerland (Schweiz)",
46008         "ch",
46009         "41"
46010       ],
46011       [
46012         "Syria (‫سوريا‬‎)",
46013         "sy",
46014         "963"
46015       ],
46016       [
46017         "Taiwan (台灣)",
46018         "tw",
46019         "886"
46020       ],
46021       [
46022         "Tajikistan",
46023         "tj",
46024         "992"
46025       ],
46026       [
46027         "Tanzania",
46028         "tz",
46029         "255"
46030       ],
46031       [
46032         "Thailand (ไทย)",
46033         "th",
46034         "66"
46035       ],
46036       [
46037         "Timor-Leste",
46038         "tl",
46039         "670"
46040       ],
46041       [
46042         "Togo",
46043         "tg",
46044         "228"
46045       ],
46046       [
46047         "Tokelau",
46048         "tk",
46049         "690"
46050       ],
46051       [
46052         "Tonga",
46053         "to",
46054         "676"
46055       ],
46056       [
46057         "Trinidad and Tobago",
46058         "tt",
46059         "1868"
46060       ],
46061       [
46062         "Tunisia (‫تونس‬‎)",
46063         "tn",
46064         "216"
46065       ],
46066       [
46067         "Turkey (Türkiye)",
46068         "tr",
46069         "90"
46070       ],
46071       [
46072         "Turkmenistan",
46073         "tm",
46074         "993"
46075       ],
46076       [
46077         "Turks and Caicos Islands",
46078         "tc",
46079         "1649"
46080       ],
46081       [
46082         "Tuvalu",
46083         "tv",
46084         "688"
46085       ],
46086       [
46087         "U.S. Virgin Islands",
46088         "vi",
46089         "1340"
46090       ],
46091       [
46092         "Uganda",
46093         "ug",
46094         "256"
46095       ],
46096       [
46097         "Ukraine (Україна)",
46098         "ua",
46099         "380"
46100       ],
46101       [
46102         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
46103         "ae",
46104         "971"
46105       ],
46106       [
46107         "United Kingdom",
46108         "gb",
46109         "44",
46110         0
46111       ],
46112       [
46113         "United States",
46114         "us",
46115         "1",
46116         0
46117       ],
46118       [
46119         "Uruguay",
46120         "uy",
46121         "598"
46122       ],
46123       [
46124         "Uzbekistan (Oʻzbekiston)",
46125         "uz",
46126         "998"
46127       ],
46128       [
46129         "Vanuatu",
46130         "vu",
46131         "678"
46132       ],
46133       [
46134         "Vatican City (Città del Vaticano)",
46135         "va",
46136         "39",
46137         1
46138       ],
46139       [
46140         "Venezuela",
46141         "ve",
46142         "58"
46143       ],
46144       [
46145         "Vietnam (Việt Nam)",
46146         "vn",
46147         "84"
46148       ],
46149       [
46150         "Wallis and Futuna (Wallis-et-Futuna)",
46151         "wf",
46152         "681"
46153       ],
46154       [
46155         "Western Sahara (‫الصحراء الغربية‬‎)",
46156         "eh",
46157         "212",
46158         1
46159       ],
46160       [
46161         "Yemen (‫اليمن‬‎)",
46162         "ye",
46163         "967"
46164       ],
46165       [
46166         "Zambia",
46167         "zm",
46168         "260"
46169       ],
46170       [
46171         "Zimbabwe",
46172         "zw",
46173         "263"
46174       ],
46175       [
46176         "Åland Islands",
46177         "ax",
46178         "358",
46179         1
46180       ]
46181   ];
46182   
46183   return d;
46184 }/**
46185 *    This script refer to:
46186 *    Title: International Telephone Input
46187 *    Author: Jack O'Connor
46188 *    Code version:  v12.1.12
46189 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46190 **/
46191
46192 /**
46193  * @class Roo.bootstrap.form.PhoneInput
46194  * @extends Roo.bootstrap.form.TriggerField
46195  * An input with International dial-code selection
46196  
46197  * @cfg {String} defaultDialCode default '+852'
46198  * @cfg {Array} preferedCountries default []
46199   
46200  * @constructor
46201  * Create a new PhoneInput.
46202  * @param {Object} config Configuration options
46203  */
46204
46205 Roo.bootstrap.form.PhoneInput = function(config) {
46206     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46207 };
46208
46209 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46210         /**
46211         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46212         */
46213         listWidth: undefined,
46214         
46215         selectedClass: 'active',
46216         
46217         invalidClass : "has-warning",
46218         
46219         validClass: 'has-success',
46220         
46221         allowed: '0123456789',
46222         
46223         max_length: 15,
46224         
46225         /**
46226          * @cfg {String} defaultDialCode The default dial code when initializing the input
46227          */
46228         defaultDialCode: '+852',
46229         
46230         /**
46231          * @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
46232          */
46233         preferedCountries: false,
46234         
46235         getAutoCreate : function()
46236         {
46237             var data = Roo.bootstrap.form.PhoneInputData();
46238             var align = this.labelAlign || this.parentLabelAlign();
46239             var id = Roo.id();
46240             
46241             this.allCountries = [];
46242             this.dialCodeMapping = [];
46243             
46244             for (var i = 0; i < data.length; i++) {
46245               var c = data[i];
46246               this.allCountries[i] = {
46247                 name: c[0],
46248                 iso2: c[1],
46249                 dialCode: c[2],
46250                 priority: c[3] || 0,
46251                 areaCodes: c[4] || null
46252               };
46253               this.dialCodeMapping[c[2]] = {
46254                   name: c[0],
46255                   iso2: c[1],
46256                   priority: c[3] || 0,
46257                   areaCodes: c[4] || null
46258               };
46259             }
46260             
46261             var cfg = {
46262                 cls: 'form-group',
46263                 cn: []
46264             };
46265             
46266             var input =  {
46267                 tag: 'input',
46268                 id : id,
46269                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46270                 maxlength: this.max_length,
46271                 cls : 'form-control tel-input',
46272                 autocomplete: 'new-password'
46273             };
46274             
46275             var hiddenInput = {
46276                 tag: 'input',
46277                 type: 'hidden',
46278                 cls: 'hidden-tel-input'
46279             };
46280             
46281             if (this.name) {
46282                 hiddenInput.name = this.name;
46283             }
46284             
46285             if (this.disabled) {
46286                 input.disabled = true;
46287             }
46288             
46289             var flag_container = {
46290                 tag: 'div',
46291                 cls: 'flag-box',
46292                 cn: [
46293                     {
46294                         tag: 'div',
46295                         cls: 'flag'
46296                     },
46297                     {
46298                         tag: 'div',
46299                         cls: 'caret'
46300                     }
46301                 ]
46302             };
46303             
46304             var box = {
46305                 tag: 'div',
46306                 cls: this.hasFeedback ? 'has-feedback' : '',
46307                 cn: [
46308                     hiddenInput,
46309                     input,
46310                     {
46311                         tag: 'input',
46312                         cls: 'dial-code-holder',
46313                         disabled: true
46314                     }
46315                 ]
46316             };
46317             
46318             var container = {
46319                 cls: 'roo-select2-container input-group',
46320                 cn: [
46321                     flag_container,
46322                     box
46323                 ]
46324             };
46325             
46326             if (this.fieldLabel.length) {
46327                 var indicator = {
46328                     tag: 'i',
46329                     tooltip: 'This field is required'
46330                 };
46331                 
46332                 var label = {
46333                     tag: 'label',
46334                     'for':  id,
46335                     cls: 'control-label',
46336                     cn: []
46337                 };
46338                 
46339                 var label_text = {
46340                     tag: 'span',
46341                     html: this.fieldLabel
46342                 };
46343                 
46344                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46345                 label.cn = [
46346                     indicator,
46347                     label_text
46348                 ];
46349                 
46350                 if(this.indicatorpos == 'right') {
46351                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46352                     label.cn = [
46353                         label_text,
46354                         indicator
46355                     ];
46356                 }
46357                 
46358                 if(align == 'left') {
46359                     container = {
46360                         tag: 'div',
46361                         cn: [
46362                             container
46363                         ]
46364                     };
46365                     
46366                     if(this.labelWidth > 12){
46367                         label.style = "width: " + this.labelWidth + 'px';
46368                     }
46369                     if(this.labelWidth < 13 && this.labelmd == 0){
46370                         this.labelmd = this.labelWidth;
46371                     }
46372                     if(this.labellg > 0){
46373                         label.cls += ' col-lg-' + this.labellg;
46374                         input.cls += ' col-lg-' + (12 - this.labellg);
46375                     }
46376                     if(this.labelmd > 0){
46377                         label.cls += ' col-md-' + this.labelmd;
46378                         container.cls += ' col-md-' + (12 - this.labelmd);
46379                     }
46380                     if(this.labelsm > 0){
46381                         label.cls += ' col-sm-' + this.labelsm;
46382                         container.cls += ' col-sm-' + (12 - this.labelsm);
46383                     }
46384                     if(this.labelxs > 0){
46385                         label.cls += ' col-xs-' + this.labelxs;
46386                         container.cls += ' col-xs-' + (12 - this.labelxs);
46387                     }
46388                 }
46389             }
46390             
46391             cfg.cn = [
46392                 label,
46393                 container
46394             ];
46395             
46396             var settings = this;
46397             
46398             ['xs','sm','md','lg'].map(function(size){
46399                 if (settings[size]) {
46400                     cfg.cls += ' col-' + size + '-' + settings[size];
46401                 }
46402             });
46403             
46404             this.store = new Roo.data.Store({
46405                 proxy : new Roo.data.MemoryProxy({}),
46406                 reader : new Roo.data.JsonReader({
46407                     fields : [
46408                         {
46409                             'name' : 'name',
46410                             'type' : 'string'
46411                         },
46412                         {
46413                             'name' : 'iso2',
46414                             'type' : 'string'
46415                         },
46416                         {
46417                             'name' : 'dialCode',
46418                             'type' : 'string'
46419                         },
46420                         {
46421                             'name' : 'priority',
46422                             'type' : 'string'
46423                         },
46424                         {
46425                             'name' : 'areaCodes',
46426                             'type' : 'string'
46427                         }
46428                     ]
46429                 })
46430             });
46431             
46432             if(!this.preferedCountries) {
46433                 this.preferedCountries = [
46434                     'hk',
46435                     'gb',
46436                     'us'
46437                 ];
46438             }
46439             
46440             var p = this.preferedCountries.reverse();
46441             
46442             if(p) {
46443                 for (var i = 0; i < p.length; i++) {
46444                     for (var j = 0; j < this.allCountries.length; j++) {
46445                         if(this.allCountries[j].iso2 == p[i]) {
46446                             var t = this.allCountries[j];
46447                             this.allCountries.splice(j,1);
46448                             this.allCountries.unshift(t);
46449                         }
46450                     } 
46451                 }
46452             }
46453             
46454             this.store.proxy.data = {
46455                 success: true,
46456                 data: this.allCountries
46457             };
46458             
46459             return cfg;
46460         },
46461         
46462         initEvents : function()
46463         {
46464             this.createList();
46465             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46466             
46467             this.indicator = this.indicatorEl();
46468             this.flag = this.flagEl();
46469             this.dialCodeHolder = this.dialCodeHolderEl();
46470             
46471             this.trigger = this.el.select('div.flag-box',true).first();
46472             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46473             
46474             var _this = this;
46475             
46476             (function(){
46477                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46478                 _this.list.setWidth(lw);
46479             }).defer(100);
46480             
46481             this.list.on('mouseover', this.onViewOver, this);
46482             this.list.on('mousemove', this.onViewMove, this);
46483             this.inputEl().on("keyup", this.onKeyUp, this);
46484             this.inputEl().on("keypress", this.onKeyPress, this);
46485             
46486             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46487
46488             this.view = new Roo.View(this.list, this.tpl, {
46489                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46490             });
46491             
46492             this.view.on('click', this.onViewClick, this);
46493             this.setValue(this.defaultDialCode);
46494         },
46495         
46496         onTriggerClick : function(e)
46497         {
46498             Roo.log('trigger click');
46499             if(this.disabled){
46500                 return;
46501             }
46502             
46503             if(this.isExpanded()){
46504                 this.collapse();
46505                 this.hasFocus = false;
46506             }else {
46507                 this.store.load({});
46508                 this.hasFocus = true;
46509                 this.expand();
46510             }
46511         },
46512         
46513         isExpanded : function()
46514         {
46515             return this.list.isVisible();
46516         },
46517         
46518         collapse : function()
46519         {
46520             if(!this.isExpanded()){
46521                 return;
46522             }
46523             this.list.hide();
46524             Roo.get(document).un('mousedown', this.collapseIf, this);
46525             Roo.get(document).un('mousewheel', this.collapseIf, this);
46526             this.fireEvent('collapse', this);
46527             this.validate();
46528         },
46529         
46530         expand : function()
46531         {
46532             Roo.log('expand');
46533
46534             if(this.isExpanded() || !this.hasFocus){
46535                 return;
46536             }
46537             
46538             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46539             this.list.setWidth(lw);
46540             
46541             this.list.show();
46542             this.restrictHeight();
46543             
46544             Roo.get(document).on('mousedown', this.collapseIf, this);
46545             Roo.get(document).on('mousewheel', this.collapseIf, this);
46546             
46547             this.fireEvent('expand', this);
46548         },
46549         
46550         restrictHeight : function()
46551         {
46552             this.list.alignTo(this.inputEl(), this.listAlign);
46553             this.list.alignTo(this.inputEl(), this.listAlign);
46554         },
46555         
46556         onViewOver : function(e, t)
46557         {
46558             if(this.inKeyMode){
46559                 return;
46560             }
46561             var item = this.view.findItemFromChild(t);
46562             
46563             if(item){
46564                 var index = this.view.indexOf(item);
46565                 this.select(index, false);
46566             }
46567         },
46568
46569         // private
46570         onViewClick : function(view, doFocus, el, e)
46571         {
46572             var index = this.view.getSelectedIndexes()[0];
46573             
46574             var r = this.store.getAt(index);
46575             
46576             if(r){
46577                 this.onSelect(r, index);
46578             }
46579             if(doFocus !== false && !this.blockFocus){
46580                 this.inputEl().focus();
46581             }
46582         },
46583         
46584         onViewMove : function(e, t)
46585         {
46586             this.inKeyMode = false;
46587         },
46588         
46589         select : function(index, scrollIntoView)
46590         {
46591             this.selectedIndex = index;
46592             this.view.select(index);
46593             if(scrollIntoView !== false){
46594                 var el = this.view.getNode(index);
46595                 if(el){
46596                     this.list.scrollChildIntoView(el, false);
46597                 }
46598             }
46599         },
46600         
46601         createList : function()
46602         {
46603             this.list = Roo.get(document.body).createChild({
46604                 tag: 'ul',
46605                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46606                 style: 'display:none'
46607             });
46608             
46609             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46610         },
46611         
46612         collapseIf : function(e)
46613         {
46614             var in_combo  = e.within(this.el);
46615             var in_list =  e.within(this.list);
46616             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46617             
46618             if (in_combo || in_list || is_list) {
46619                 return;
46620             }
46621             this.collapse();
46622         },
46623         
46624         onSelect : function(record, index)
46625         {
46626             if(this.fireEvent('beforeselect', this, record, index) !== false){
46627                 
46628                 this.setFlagClass(record.data.iso2);
46629                 this.setDialCode(record.data.dialCode);
46630                 this.hasFocus = false;
46631                 this.collapse();
46632                 this.fireEvent('select', this, record, index);
46633             }
46634         },
46635         
46636         flagEl : function()
46637         {
46638             var flag = this.el.select('div.flag',true).first();
46639             if(!flag){
46640                 return false;
46641             }
46642             return flag;
46643         },
46644         
46645         dialCodeHolderEl : function()
46646         {
46647             var d = this.el.select('input.dial-code-holder',true).first();
46648             if(!d){
46649                 return false;
46650             }
46651             return d;
46652         },
46653         
46654         setDialCode : function(v)
46655         {
46656             this.dialCodeHolder.dom.value = '+'+v;
46657         },
46658         
46659         setFlagClass : function(n)
46660         {
46661             this.flag.dom.className = 'flag '+n;
46662         },
46663         
46664         getValue : function()
46665         {
46666             var v = this.inputEl().getValue();
46667             if(this.dialCodeHolder) {
46668                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46669             }
46670             return v;
46671         },
46672         
46673         setValue : function(v)
46674         {
46675             var d = this.getDialCode(v);
46676             
46677             //invalid dial code
46678             if(v.length == 0 || !d || d.length == 0) {
46679                 if(this.rendered){
46680                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46681                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46682                 }
46683                 return;
46684             }
46685             
46686             //valid dial code
46687             this.setFlagClass(this.dialCodeMapping[d].iso2);
46688             this.setDialCode(d);
46689             this.inputEl().dom.value = v.replace('+'+d,'');
46690             this.hiddenEl().dom.value = this.getValue();
46691             
46692             this.validate();
46693         },
46694         
46695         getDialCode : function(v)
46696         {
46697             v = v ||  '';
46698             
46699             if (v.length == 0) {
46700                 return this.dialCodeHolder.dom.value;
46701             }
46702             
46703             var dialCode = "";
46704             if (v.charAt(0) != "+") {
46705                 return false;
46706             }
46707             var numericChars = "";
46708             for (var i = 1; i < v.length; i++) {
46709               var c = v.charAt(i);
46710               if (!isNaN(c)) {
46711                 numericChars += c;
46712                 if (this.dialCodeMapping[numericChars]) {
46713                   dialCode = v.substr(1, i);
46714                 }
46715                 if (numericChars.length == 4) {
46716                   break;
46717                 }
46718               }
46719             }
46720             return dialCode;
46721         },
46722         
46723         reset : function()
46724         {
46725             this.setValue(this.defaultDialCode);
46726             this.validate();
46727         },
46728         
46729         hiddenEl : function()
46730         {
46731             return this.el.select('input.hidden-tel-input',true).first();
46732         },
46733         
46734         // after setting val
46735         onKeyUp : function(e){
46736             this.setValue(this.getValue());
46737         },
46738         
46739         onKeyPress : function(e){
46740             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46741                 e.stopEvent();
46742             }
46743         }
46744         
46745 });
46746 /**
46747  * @class Roo.bootstrap.form.MoneyField
46748  * @extends Roo.bootstrap.form.ComboBox
46749  * Bootstrap MoneyField class
46750  * 
46751  * @constructor
46752  * Create a new MoneyField.
46753  * @param {Object} config Configuration options
46754  */
46755
46756 Roo.bootstrap.form.MoneyField = function(config) {
46757     
46758     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46759     
46760 };
46761
46762 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46763     
46764     /**
46765      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46766      */
46767     allowDecimals : true,
46768     /**
46769      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46770      */
46771     decimalSeparator : ".",
46772     /**
46773      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46774      */
46775     decimalPrecision : 0,
46776     /**
46777      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46778      */
46779     allowNegative : true,
46780     /**
46781      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46782      */
46783     allowZero: true,
46784     /**
46785      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46786      */
46787     minValue : Number.NEGATIVE_INFINITY,
46788     /**
46789      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46790      */
46791     maxValue : Number.MAX_VALUE,
46792     /**
46793      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46794      */
46795     minText : "The minimum value for this field is {0}",
46796     /**
46797      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46798      */
46799     maxText : "The maximum value for this field is {0}",
46800     /**
46801      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46802      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46803      */
46804     nanText : "{0} is not a valid number",
46805     /**
46806      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46807      */
46808     castInt : true,
46809     /**
46810      * @cfg {String} defaults currency of the MoneyField
46811      * value should be in lkey
46812      */
46813     defaultCurrency : false,
46814     /**
46815      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46816      */
46817     thousandsDelimiter : false,
46818     /**
46819      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46820      */
46821     max_length: false,
46822     
46823     inputlg : 9,
46824     inputmd : 9,
46825     inputsm : 9,
46826     inputxs : 6,
46827      /**
46828      * @cfg {Roo.data.Store} store  Store to lookup currency??
46829      */
46830     store : false,
46831     
46832     getAutoCreate : function()
46833     {
46834         var align = this.labelAlign || this.parentLabelAlign();
46835         
46836         var id = Roo.id();
46837
46838         var cfg = {
46839             cls: 'form-group',
46840             cn: []
46841         };
46842
46843         var input =  {
46844             tag: 'input',
46845             id : id,
46846             cls : 'form-control roo-money-amount-input',
46847             autocomplete: 'new-password'
46848         };
46849         
46850         var hiddenInput = {
46851             tag: 'input',
46852             type: 'hidden',
46853             id: Roo.id(),
46854             cls: 'hidden-number-input'
46855         };
46856         
46857         if(this.max_length) {
46858             input.maxlength = this.max_length; 
46859         }
46860         
46861         if (this.name) {
46862             hiddenInput.name = this.name;
46863         }
46864
46865         if (this.disabled) {
46866             input.disabled = true;
46867         }
46868
46869         var clg = 12 - this.inputlg;
46870         var cmd = 12 - this.inputmd;
46871         var csm = 12 - this.inputsm;
46872         var cxs = 12 - this.inputxs;
46873         
46874         var container = {
46875             tag : 'div',
46876             cls : 'row roo-money-field',
46877             cn : [
46878                 {
46879                     tag : 'div',
46880                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46881                     cn : [
46882                         {
46883                             tag : 'div',
46884                             cls: 'roo-select2-container input-group',
46885                             cn: [
46886                                 {
46887                                     tag : 'input',
46888                                     cls : 'form-control roo-money-currency-input',
46889                                     autocomplete: 'new-password',
46890                                     readOnly : 1,
46891                                     name : this.currencyName
46892                                 },
46893                                 {
46894                                     tag :'span',
46895                                     cls : 'input-group-addon',
46896                                     cn : [
46897                                         {
46898                                             tag: 'span',
46899                                             cls: 'caret'
46900                                         }
46901                                     ]
46902                                 }
46903                             ]
46904                         }
46905                     ]
46906                 },
46907                 {
46908                     tag : 'div',
46909                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46910                     cn : [
46911                         {
46912                             tag: 'div',
46913                             cls: this.hasFeedback ? 'has-feedback' : '',
46914                             cn: [
46915                                 input
46916                             ]
46917                         }
46918                     ]
46919                 }
46920             ]
46921             
46922         };
46923         
46924         if (this.fieldLabel.length) {
46925             var indicator = {
46926                 tag: 'i',
46927                 tooltip: 'This field is required'
46928             };
46929
46930             var label = {
46931                 tag: 'label',
46932                 'for':  id,
46933                 cls: 'control-label',
46934                 cn: []
46935             };
46936
46937             var label_text = {
46938                 tag: 'span',
46939                 html: this.fieldLabel
46940             };
46941
46942             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46943             label.cn = [
46944                 indicator,
46945                 label_text
46946             ];
46947
46948             if(this.indicatorpos == 'right') {
46949                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46950                 label.cn = [
46951                     label_text,
46952                     indicator
46953                 ];
46954             }
46955
46956             if(align == 'left') {
46957                 container = {
46958                     tag: 'div',
46959                     cn: [
46960                         container
46961                     ]
46962                 };
46963
46964                 if(this.labelWidth > 12){
46965                     label.style = "width: " + this.labelWidth + 'px';
46966                 }
46967                 if(this.labelWidth < 13 && this.labelmd == 0){
46968                     this.labelmd = this.labelWidth;
46969                 }
46970                 if(this.labellg > 0){
46971                     label.cls += ' col-lg-' + this.labellg;
46972                     input.cls += ' col-lg-' + (12 - this.labellg);
46973                 }
46974                 if(this.labelmd > 0){
46975                     label.cls += ' col-md-' + this.labelmd;
46976                     container.cls += ' col-md-' + (12 - this.labelmd);
46977                 }
46978                 if(this.labelsm > 0){
46979                     label.cls += ' col-sm-' + this.labelsm;
46980                     container.cls += ' col-sm-' + (12 - this.labelsm);
46981                 }
46982                 if(this.labelxs > 0){
46983                     label.cls += ' col-xs-' + this.labelxs;
46984                     container.cls += ' col-xs-' + (12 - this.labelxs);
46985                 }
46986             }
46987         }
46988
46989         cfg.cn = [
46990             label,
46991             container,
46992             hiddenInput
46993         ];
46994         
46995         var settings = this;
46996
46997         ['xs','sm','md','lg'].map(function(size){
46998             if (settings[size]) {
46999                 cfg.cls += ' col-' + size + '-' + settings[size];
47000             }
47001         });
47002         
47003         return cfg;
47004     },
47005     
47006     initEvents : function()
47007     {
47008         this.indicator = this.indicatorEl();
47009         
47010         this.initCurrencyEvent();
47011         
47012         this.initNumberEvent();
47013     },
47014     
47015     initCurrencyEvent : function()
47016     {
47017         if (!this.store) {
47018             throw "can not find store for combo";
47019         }
47020         
47021         this.store = Roo.factory(this.store, Roo.data);
47022         this.store.parent = this;
47023         
47024         this.createList();
47025         
47026         this.triggerEl = this.el.select('.input-group-addon', true).first();
47027         
47028         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47029         
47030         var _this = this;
47031         
47032         (function(){
47033             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47034             _this.list.setWidth(lw);
47035         }).defer(100);
47036         
47037         this.list.on('mouseover', this.onViewOver, this);
47038         this.list.on('mousemove', this.onViewMove, this);
47039         this.list.on('scroll', this.onViewScroll, this);
47040         
47041         if(!this.tpl){
47042             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47043         }
47044         
47045         this.view = new Roo.View(this.list, this.tpl, {
47046             singleSelect:true, store: this.store, selectedClass: this.selectedClass
47047         });
47048         
47049         this.view.on('click', this.onViewClick, this);
47050         
47051         this.store.on('beforeload', this.onBeforeLoad, this);
47052         this.store.on('load', this.onLoad, this);
47053         this.store.on('loadexception', this.onLoadException, this);
47054         
47055         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47056             "up" : function(e){
47057                 this.inKeyMode = true;
47058                 this.selectPrev();
47059             },
47060
47061             "down" : function(e){
47062                 if(!this.isExpanded()){
47063                     this.onTriggerClick();
47064                 }else{
47065                     this.inKeyMode = true;
47066                     this.selectNext();
47067                 }
47068             },
47069
47070             "enter" : function(e){
47071                 this.collapse();
47072                 
47073                 if(this.fireEvent("specialkey", this, e)){
47074                     this.onViewClick(false);
47075                 }
47076                 
47077                 return true;
47078             },
47079
47080             "esc" : function(e){
47081                 this.collapse();
47082             },
47083
47084             "tab" : function(e){
47085                 this.collapse();
47086                 
47087                 if(this.fireEvent("specialkey", this, e)){
47088                     this.onViewClick(false);
47089                 }
47090                 
47091                 return true;
47092             },
47093
47094             scope : this,
47095
47096             doRelay : function(foo, bar, hname){
47097                 if(hname == 'down' || this.scope.isExpanded()){
47098                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47099                 }
47100                 return true;
47101             },
47102
47103             forceKeyDown: true
47104         });
47105         
47106         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47107         
47108     },
47109     
47110     initNumberEvent : function(e)
47111     {
47112         this.inputEl().on("keydown" , this.fireKey,  this);
47113         this.inputEl().on("focus", this.onFocus,  this);
47114         this.inputEl().on("blur", this.onBlur,  this);
47115         
47116         this.inputEl().relayEvent('keyup', this);
47117         
47118         if(this.indicator){
47119             this.indicator.addClass('invisible');
47120         }
47121  
47122         this.originalValue = this.getValue();
47123         
47124         if(this.validationEvent == 'keyup'){
47125             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47126             this.inputEl().on('keyup', this.filterValidation, this);
47127         }
47128         else if(this.validationEvent !== false){
47129             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47130         }
47131         
47132         if(this.selectOnFocus){
47133             this.on("focus", this.preFocus, this);
47134             
47135         }
47136         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47137             this.inputEl().on("keypress", this.filterKeys, this);
47138         } else {
47139             this.inputEl().relayEvent('keypress', this);
47140         }
47141         
47142         var allowed = "0123456789";
47143         
47144         if(this.allowDecimals){
47145             allowed += this.decimalSeparator;
47146         }
47147         
47148         if(this.allowNegative){
47149             allowed += "-";
47150         }
47151         
47152         if(this.thousandsDelimiter) {
47153             allowed += ",";
47154         }
47155         
47156         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47157         
47158         var keyPress = function(e){
47159             
47160             var k = e.getKey();
47161             
47162             var c = e.getCharCode();
47163             
47164             if(
47165                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47166                     allowed.indexOf(String.fromCharCode(c)) === -1
47167             ){
47168                 e.stopEvent();
47169                 return;
47170             }
47171             
47172             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47173                 return;
47174             }
47175             
47176             if(allowed.indexOf(String.fromCharCode(c)) === -1){
47177                 e.stopEvent();
47178             }
47179         };
47180         
47181         this.inputEl().on("keypress", keyPress, this);
47182         
47183     },
47184     
47185     onTriggerClick : function(e)
47186     {   
47187         if(this.disabled){
47188             return;
47189         }
47190         
47191         this.page = 0;
47192         this.loadNext = false;
47193         
47194         if(this.isExpanded()){
47195             this.collapse();
47196             return;
47197         }
47198         
47199         this.hasFocus = true;
47200         
47201         if(this.triggerAction == 'all') {
47202             this.doQuery(this.allQuery, true);
47203             return;
47204         }
47205         
47206         this.doQuery(this.getRawValue());
47207     },
47208     
47209     getCurrency : function()
47210     {   
47211         var v = this.currencyEl().getValue();
47212         
47213         return v;
47214     },
47215     
47216     restrictHeight : function()
47217     {
47218         this.list.alignTo(this.currencyEl(), this.listAlign);
47219         this.list.alignTo(this.currencyEl(), this.listAlign);
47220     },
47221     
47222     onViewClick : function(view, doFocus, el, e)
47223     {
47224         var index = this.view.getSelectedIndexes()[0];
47225         
47226         var r = this.store.getAt(index);
47227         
47228         if(r){
47229             this.onSelect(r, index);
47230         }
47231     },
47232     
47233     onSelect : function(record, index){
47234         
47235         if(this.fireEvent('beforeselect', this, record, index) !== false){
47236         
47237             this.setFromCurrencyData(index > -1 ? record.data : false);
47238             
47239             this.collapse();
47240             
47241             this.fireEvent('select', this, record, index);
47242         }
47243     },
47244     
47245     setFromCurrencyData : function(o)
47246     {
47247         var currency = '';
47248         
47249         this.lastCurrency = o;
47250         
47251         if (this.currencyField) {
47252             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47253         } else {
47254             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47255         }
47256         
47257         this.lastSelectionText = currency;
47258         
47259         //setting default currency
47260         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47261             this.setCurrency(this.defaultCurrency);
47262             return;
47263         }
47264         
47265         this.setCurrency(currency);
47266     },
47267     
47268     setFromData : function(o)
47269     {
47270         var c = {};
47271         
47272         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47273         
47274         this.setFromCurrencyData(c);
47275         
47276         var value = '';
47277         
47278         if (this.name) {
47279             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47280         } else {
47281             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47282         }
47283         
47284         this.setValue(value);
47285         
47286     },
47287     
47288     setCurrency : function(v)
47289     {   
47290         this.currencyValue = v;
47291         
47292         if(this.rendered){
47293             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47294             this.validate();
47295         }
47296     },
47297     
47298     setValue : function(v)
47299     {
47300         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47301         
47302         this.value = v;
47303         
47304         if(this.rendered){
47305             
47306             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47307             
47308             this.inputEl().dom.value = (v == '') ? '' :
47309                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47310             
47311             if(!this.allowZero && v === '0') {
47312                 this.hiddenEl().dom.value = '';
47313                 this.inputEl().dom.value = '';
47314             }
47315             
47316             this.validate();
47317         }
47318     },
47319     
47320     getRawValue : function()
47321     {
47322         var v = this.inputEl().getValue();
47323         
47324         return v;
47325     },
47326     
47327     getValue : function()
47328     {
47329         return this.fixPrecision(this.parseValue(this.getRawValue()));
47330     },
47331     
47332     parseValue : function(value)
47333     {
47334         if(this.thousandsDelimiter) {
47335             value += "";
47336             r = new RegExp(",", "g");
47337             value = value.replace(r, "");
47338         }
47339         
47340         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47341         return isNaN(value) ? '' : value;
47342         
47343     },
47344     
47345     fixPrecision : function(value)
47346     {
47347         if(this.thousandsDelimiter) {
47348             value += "";
47349             r = new RegExp(",", "g");
47350             value = value.replace(r, "");
47351         }
47352         
47353         var nan = isNaN(value);
47354         
47355         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47356             return nan ? '' : value;
47357         }
47358         return parseFloat(value).toFixed(this.decimalPrecision);
47359     },
47360     
47361     decimalPrecisionFcn : function(v)
47362     {
47363         return Math.floor(v);
47364     },
47365     
47366     validateValue : function(value)
47367     {
47368         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47369             return false;
47370         }
47371         
47372         var num = this.parseValue(value);
47373         
47374         if(isNaN(num)){
47375             this.markInvalid(String.format(this.nanText, value));
47376             return false;
47377         }
47378         
47379         if(num < this.minValue){
47380             this.markInvalid(String.format(this.minText, this.minValue));
47381             return false;
47382         }
47383         
47384         if(num > this.maxValue){
47385             this.markInvalid(String.format(this.maxText, this.maxValue));
47386             return false;
47387         }
47388         
47389         return true;
47390     },
47391     
47392     validate : function()
47393     {
47394         if(this.disabled || this.allowBlank){
47395             this.markValid();
47396             return true;
47397         }
47398         
47399         var currency = this.getCurrency();
47400         
47401         if(this.validateValue(this.getRawValue()) && currency.length){
47402             this.markValid();
47403             return true;
47404         }
47405         
47406         this.markInvalid();
47407         return false;
47408     },
47409     
47410     getName: function()
47411     {
47412         return this.name;
47413     },
47414     
47415     beforeBlur : function()
47416     {
47417         if(!this.castInt){
47418             return;
47419         }
47420         
47421         var v = this.parseValue(this.getRawValue());
47422         
47423         if(v || v == 0){
47424             this.setValue(v);
47425         }
47426     },
47427     
47428     onBlur : function()
47429     {
47430         this.beforeBlur();
47431         
47432         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47433             //this.el.removeClass(this.focusClass);
47434         }
47435         
47436         this.hasFocus = false;
47437         
47438         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47439             this.validate();
47440         }
47441         
47442         var v = this.getValue();
47443         
47444         if(String(v) !== String(this.startValue)){
47445             this.fireEvent('change', this, v, this.startValue);
47446         }
47447         
47448         this.fireEvent("blur", this);
47449     },
47450     
47451     inputEl : function()
47452     {
47453         return this.el.select('.roo-money-amount-input', true).first();
47454     },
47455     
47456     currencyEl : function()
47457     {
47458         return this.el.select('.roo-money-currency-input', true).first();
47459     },
47460     
47461     hiddenEl : function()
47462     {
47463         return this.el.select('input.hidden-number-input',true).first();
47464     }
47465     
47466 });/**
47467  * @class Roo.bootstrap.BezierSignature
47468  * @extends Roo.bootstrap.Component
47469  * Bootstrap BezierSignature class
47470  * This script refer to:
47471  *    Title: Signature Pad
47472  *    Author: szimek
47473  *    Availability: https://github.com/szimek/signature_pad
47474  *
47475  * @constructor
47476  * Create a new BezierSignature
47477  * @param {Object} config The config object
47478  */
47479
47480 Roo.bootstrap.BezierSignature = function(config){
47481     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47482     this.addEvents({
47483         "resize" : true
47484     });
47485 };
47486
47487 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47488 {
47489      
47490     curve_data: [],
47491     
47492     is_empty: true,
47493     
47494     mouse_btn_down: true,
47495     
47496     /**
47497      * @cfg {int} canvas height
47498      */
47499     canvas_height: '200px',
47500     
47501     /**
47502      * @cfg {float|function} Radius of a single dot.
47503      */ 
47504     dot_size: false,
47505     
47506     /**
47507      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47508      */
47509     min_width: 0.5,
47510     
47511     /**
47512      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47513      */
47514     max_width: 2.5,
47515     
47516     /**
47517      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47518      */
47519     throttle: 16,
47520     
47521     /**
47522      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47523      */
47524     min_distance: 5,
47525     
47526     /**
47527      * @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.
47528      */
47529     bg_color: 'rgba(0, 0, 0, 0)',
47530     
47531     /**
47532      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47533      */
47534     dot_color: 'black',
47535     
47536     /**
47537      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47538      */ 
47539     velocity_filter_weight: 0.7,
47540     
47541     /**
47542      * @cfg {function} Callback when stroke begin. 
47543      */
47544     onBegin: false,
47545     
47546     /**
47547      * @cfg {function} Callback when stroke end.
47548      */
47549     onEnd: false,
47550     
47551     getAutoCreate : function()
47552     {
47553         var cls = 'roo-signature column';
47554         
47555         if(this.cls){
47556             cls += ' ' + this.cls;
47557         }
47558         
47559         var col_sizes = [
47560             'lg',
47561             'md',
47562             'sm',
47563             'xs'
47564         ];
47565         
47566         for(var i = 0; i < col_sizes.length; i++) {
47567             if(this[col_sizes[i]]) {
47568                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47569             }
47570         }
47571         
47572         var cfg = {
47573             tag: 'div',
47574             cls: cls,
47575             cn: [
47576                 {
47577                     tag: 'div',
47578                     cls: 'roo-signature-body',
47579                     cn: [
47580                         {
47581                             tag: 'canvas',
47582                             cls: 'roo-signature-body-canvas',
47583                             height: this.canvas_height,
47584                             width: this.canvas_width
47585                         }
47586                     ]
47587                 },
47588                 {
47589                     tag: 'input',
47590                     type: 'file',
47591                     style: 'display: none'
47592                 }
47593             ]
47594         };
47595         
47596         return cfg;
47597     },
47598     
47599     initEvents: function() 
47600     {
47601         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47602         
47603         var canvas = this.canvasEl();
47604         
47605         // mouse && touch event swapping...
47606         canvas.dom.style.touchAction = 'none';
47607         canvas.dom.style.msTouchAction = 'none';
47608         
47609         this.mouse_btn_down = false;
47610         canvas.on('mousedown', this._handleMouseDown, this);
47611         canvas.on('mousemove', this._handleMouseMove, this);
47612         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47613         
47614         if (window.PointerEvent) {
47615             canvas.on('pointerdown', this._handleMouseDown, this);
47616             canvas.on('pointermove', this._handleMouseMove, this);
47617             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47618         }
47619         
47620         if ('ontouchstart' in window) {
47621             canvas.on('touchstart', this._handleTouchStart, this);
47622             canvas.on('touchmove', this._handleTouchMove, this);
47623             canvas.on('touchend', this._handleTouchEnd, this);
47624         }
47625         
47626         Roo.EventManager.onWindowResize(this.resize, this, true);
47627         
47628         // file input event
47629         this.fileEl().on('change', this.uploadImage, this);
47630         
47631         this.clear();
47632         
47633         this.resize();
47634     },
47635     
47636     resize: function(){
47637         
47638         var canvas = this.canvasEl().dom;
47639         var ctx = this.canvasElCtx();
47640         var img_data = false;
47641         
47642         if(canvas.width > 0) {
47643             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47644         }
47645         // setting canvas width will clean img data
47646         canvas.width = 0;
47647         
47648         var style = window.getComputedStyle ? 
47649             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47650             
47651         var padding_left = parseInt(style.paddingLeft) || 0;
47652         var padding_right = parseInt(style.paddingRight) || 0;
47653         
47654         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47655         
47656         if(img_data) {
47657             ctx.putImageData(img_data, 0, 0);
47658         }
47659     },
47660     
47661     _handleMouseDown: function(e)
47662     {
47663         if (e.browserEvent.which === 1) {
47664             this.mouse_btn_down = true;
47665             this.strokeBegin(e);
47666         }
47667     },
47668     
47669     _handleMouseMove: function (e)
47670     {
47671         if (this.mouse_btn_down) {
47672             this.strokeMoveUpdate(e);
47673         }
47674     },
47675     
47676     _handleMouseUp: function (e)
47677     {
47678         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47679             this.mouse_btn_down = false;
47680             this.strokeEnd(e);
47681         }
47682     },
47683     
47684     _handleTouchStart: function (e) {
47685         
47686         e.preventDefault();
47687         if (e.browserEvent.targetTouches.length === 1) {
47688             // var touch = e.browserEvent.changedTouches[0];
47689             // this.strokeBegin(touch);
47690             
47691              this.strokeBegin(e); // assume e catching the correct xy...
47692         }
47693     },
47694     
47695     _handleTouchMove: function (e) {
47696         e.preventDefault();
47697         // var touch = event.targetTouches[0];
47698         // _this._strokeMoveUpdate(touch);
47699         this.strokeMoveUpdate(e);
47700     },
47701     
47702     _handleTouchEnd: function (e) {
47703         var wasCanvasTouched = e.target === this.canvasEl().dom;
47704         if (wasCanvasTouched) {
47705             e.preventDefault();
47706             // var touch = event.changedTouches[0];
47707             // _this._strokeEnd(touch);
47708             this.strokeEnd(e);
47709         }
47710     },
47711     
47712     reset: function () {
47713         this._lastPoints = [];
47714         this._lastVelocity = 0;
47715         this._lastWidth = (this.min_width + this.max_width) / 2;
47716         this.canvasElCtx().fillStyle = this.dot_color;
47717     },
47718     
47719     strokeMoveUpdate: function(e)
47720     {
47721         this.strokeUpdate(e);
47722         
47723         if (this.throttle) {
47724             this.throttleStroke(this.strokeUpdate, this.throttle);
47725         }
47726         else {
47727             this.strokeUpdate(e);
47728         }
47729     },
47730     
47731     strokeBegin: function(e)
47732     {
47733         var newPointGroup = {
47734             color: this.dot_color,
47735             points: []
47736         };
47737         
47738         if (typeof this.onBegin === 'function') {
47739             this.onBegin(e);
47740         }
47741         
47742         this.curve_data.push(newPointGroup);
47743         this.reset();
47744         this.strokeUpdate(e);
47745     },
47746     
47747     strokeUpdate: function(e)
47748     {
47749         var rect = this.canvasEl().dom.getBoundingClientRect();
47750         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47751         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47752         var lastPoints = lastPointGroup.points;
47753         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47754         var isLastPointTooClose = lastPoint
47755             ? point.distanceTo(lastPoint) <= this.min_distance
47756             : false;
47757         var color = lastPointGroup.color;
47758         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47759             var curve = this.addPoint(point);
47760             if (!lastPoint) {
47761                 this.drawDot({color: color, point: point});
47762             }
47763             else if (curve) {
47764                 this.drawCurve({color: color, curve: curve});
47765             }
47766             lastPoints.push({
47767                 time: point.time,
47768                 x: point.x,
47769                 y: point.y
47770             });
47771         }
47772     },
47773     
47774     strokeEnd: function(e)
47775     {
47776         this.strokeUpdate(e);
47777         if (typeof this.onEnd === 'function') {
47778             this.onEnd(e);
47779         }
47780     },
47781     
47782     addPoint:  function (point) {
47783         var _lastPoints = this._lastPoints;
47784         _lastPoints.push(point);
47785         if (_lastPoints.length > 2) {
47786             if (_lastPoints.length === 3) {
47787                 _lastPoints.unshift(_lastPoints[0]);
47788             }
47789             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47790             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47791             _lastPoints.shift();
47792             return curve;
47793         }
47794         return null;
47795     },
47796     
47797     calculateCurveWidths: function (startPoint, endPoint) {
47798         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47799             (1 - this.velocity_filter_weight) * this._lastVelocity;
47800
47801         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47802         var widths = {
47803             end: newWidth,
47804             start: this._lastWidth
47805         };
47806         
47807         this._lastVelocity = velocity;
47808         this._lastWidth = newWidth;
47809         return widths;
47810     },
47811     
47812     drawDot: function (_a) {
47813         var color = _a.color, point = _a.point;
47814         var ctx = this.canvasElCtx();
47815         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47816         ctx.beginPath();
47817         this.drawCurveSegment(point.x, point.y, width);
47818         ctx.closePath();
47819         ctx.fillStyle = color;
47820         ctx.fill();
47821     },
47822     
47823     drawCurve: function (_a) {
47824         var color = _a.color, curve = _a.curve;
47825         var ctx = this.canvasElCtx();
47826         var widthDelta = curve.endWidth - curve.startWidth;
47827         var drawSteps = Math.floor(curve.length()) * 2;
47828         ctx.beginPath();
47829         ctx.fillStyle = color;
47830         for (var i = 0; i < drawSteps; i += 1) {
47831         var t = i / drawSteps;
47832         var tt = t * t;
47833         var ttt = tt * t;
47834         var u = 1 - t;
47835         var uu = u * u;
47836         var uuu = uu * u;
47837         var x = uuu * curve.startPoint.x;
47838         x += 3 * uu * t * curve.control1.x;
47839         x += 3 * u * tt * curve.control2.x;
47840         x += ttt * curve.endPoint.x;
47841         var y = uuu * curve.startPoint.y;
47842         y += 3 * uu * t * curve.control1.y;
47843         y += 3 * u * tt * curve.control2.y;
47844         y += ttt * curve.endPoint.y;
47845         var width = curve.startWidth + ttt * widthDelta;
47846         this.drawCurveSegment(x, y, width);
47847         }
47848         ctx.closePath();
47849         ctx.fill();
47850     },
47851     
47852     drawCurveSegment: function (x, y, width) {
47853         var ctx = this.canvasElCtx();
47854         ctx.moveTo(x, y);
47855         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47856         this.is_empty = false;
47857     },
47858     
47859     clear: function()
47860     {
47861         var ctx = this.canvasElCtx();
47862         var canvas = this.canvasEl().dom;
47863         ctx.fillStyle = this.bg_color;
47864         ctx.clearRect(0, 0, canvas.width, canvas.height);
47865         ctx.fillRect(0, 0, canvas.width, canvas.height);
47866         this.curve_data = [];
47867         this.reset();
47868         this.is_empty = true;
47869     },
47870     
47871     fileEl: function()
47872     {
47873         return  this.el.select('input',true).first();
47874     },
47875     
47876     canvasEl: function()
47877     {
47878         return this.el.select('canvas',true).first();
47879     },
47880     
47881     canvasElCtx: function()
47882     {
47883         return this.el.select('canvas',true).first().dom.getContext('2d');
47884     },
47885     
47886     getImage: function(type)
47887     {
47888         if(this.is_empty) {
47889             return false;
47890         }
47891         
47892         // encryption ?
47893         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47894     },
47895     
47896     drawFromImage: function(img_src)
47897     {
47898         var img = new Image();
47899         
47900         img.onload = function(){
47901             this.canvasElCtx().drawImage(img, 0, 0);
47902         }.bind(this);
47903         
47904         img.src = img_src;
47905         
47906         this.is_empty = false;
47907     },
47908     
47909     selectImage: function()
47910     {
47911         this.fileEl().dom.click();
47912     },
47913     
47914     uploadImage: function(e)
47915     {
47916         var reader = new FileReader();
47917         
47918         reader.onload = function(e){
47919             var img = new Image();
47920             img.onload = function(){
47921                 this.reset();
47922                 this.canvasElCtx().drawImage(img, 0, 0);
47923             }.bind(this);
47924             img.src = e.target.result;
47925         }.bind(this);
47926         
47927         reader.readAsDataURL(e.target.files[0]);
47928     },
47929     
47930     // Bezier Point Constructor
47931     Point: (function () {
47932         function Point(x, y, time) {
47933             this.x = x;
47934             this.y = y;
47935             this.time = time || Date.now();
47936         }
47937         Point.prototype.distanceTo = function (start) {
47938             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47939         };
47940         Point.prototype.equals = function (other) {
47941             return this.x === other.x && this.y === other.y && this.time === other.time;
47942         };
47943         Point.prototype.velocityFrom = function (start) {
47944             return this.time !== start.time
47945             ? this.distanceTo(start) / (this.time - start.time)
47946             : 0;
47947         };
47948         return Point;
47949     }()),
47950     
47951     
47952     // Bezier Constructor
47953     Bezier: (function () {
47954         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47955             this.startPoint = startPoint;
47956             this.control2 = control2;
47957             this.control1 = control1;
47958             this.endPoint = endPoint;
47959             this.startWidth = startWidth;
47960             this.endWidth = endWidth;
47961         }
47962         Bezier.fromPoints = function (points, widths, scope) {
47963             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47964             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47965             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47966         };
47967         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47968             var dx1 = s1.x - s2.x;
47969             var dy1 = s1.y - s2.y;
47970             var dx2 = s2.x - s3.x;
47971             var dy2 = s2.y - s3.y;
47972             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47973             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47974             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47975             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47976             var dxm = m1.x - m2.x;
47977             var dym = m1.y - m2.y;
47978             var k = l2 / (l1 + l2);
47979             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47980             var tx = s2.x - cm.x;
47981             var ty = s2.y - cm.y;
47982             return {
47983                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47984                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47985             };
47986         };
47987         Bezier.prototype.length = function () {
47988             var steps = 10;
47989             var length = 0;
47990             var px;
47991             var py;
47992             for (var i = 0; i <= steps; i += 1) {
47993                 var t = i / steps;
47994                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47995                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47996                 if (i > 0) {
47997                     var xdiff = cx - px;
47998                     var ydiff = cy - py;
47999                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
48000                 }
48001                 px = cx;
48002                 py = cy;
48003             }
48004             return length;
48005         };
48006         Bezier.prototype.point = function (t, start, c1, c2, end) {
48007             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48008             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48009             + (3.0 * c2 * (1.0 - t) * t * t)
48010             + (end * t * t * t);
48011         };
48012         return Bezier;
48013     }()),
48014     
48015     throttleStroke: function(fn, wait) {
48016       if (wait === void 0) { wait = 250; }
48017       var previous = 0;
48018       var timeout = null;
48019       var result;
48020       var storedContext;
48021       var storedArgs;
48022       var later = function () {
48023           previous = Date.now();
48024           timeout = null;
48025           result = fn.apply(storedContext, storedArgs);
48026           if (!timeout) {
48027               storedContext = null;
48028               storedArgs = [];
48029           }
48030       };
48031       return function wrapper() {
48032           var args = [];
48033           for (var _i = 0; _i < arguments.length; _i++) {
48034               args[_i] = arguments[_i];
48035           }
48036           var now = Date.now();
48037           var remaining = wait - (now - previous);
48038           storedContext = this;
48039           storedArgs = args;
48040           if (remaining <= 0 || remaining > wait) {
48041               if (timeout) {
48042                   clearTimeout(timeout);
48043                   timeout = null;
48044               }
48045               previous = now;
48046               result = fn.apply(storedContext, storedArgs);
48047               if (!timeout) {
48048                   storedContext = null;
48049                   storedArgs = [];
48050               }
48051           }
48052           else if (!timeout) {
48053               timeout = window.setTimeout(later, remaining);
48054           }
48055           return result;
48056       };
48057   }
48058   
48059 });
48060
48061  
48062
48063  // old names for form elements
48064 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
48065 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
48066 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
48067 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
48068 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
48069 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
48070 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
48071 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
48072 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
48073 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
48074 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
48075 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
48076 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
48077 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
48078 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
48079 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
48080 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
48081 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
48082 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
48083 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
48084 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
48085 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
48086 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
48087 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
48088 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
48089 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
48090
48091 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
48092 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48093
48094 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
48095 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
48096
48097 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
48098 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48099 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
48100 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
48101