54ab469daed3f511078e7567986da09343958c45
[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 autoSize() and initCSS()
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             return;
9510         }
9511         
9512         var cm = this.cm, styles = [];
9513         this.CSS.removeStyleSheet(this.id + '-cssrules');
9514         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9515         // we can honour xs/sm/md/xl  as widths...
9516         // we first have to decide what widht we are currently at...
9517         var sz = Roo.getGridSize();
9518         
9519         var total = 0;
9520         var last = -1;
9521         var cols = []; // visable cols.
9522         var total_abs = 0;
9523         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9524             var w = cm.getColumnWidth(i, false);
9525             if(cm.isHidden(i)){
9526                 cols.push( { rel : false, abs : 0 });
9527                 continue;
9528             }
9529             if (w !== false) {
9530                 cols.push( { rel : false, abs : w });
9531                 total_abs += w;
9532                 last = i; // not really..
9533                 continue;
9534             }
9535             var w = cm.getColumnWidth(i, sz);
9536             if (w > 0) {
9537                 last = i
9538             }
9539             total += w;
9540             cols.push( { rel : w, abs : false });
9541         }
9542         
9543         var avail = this.bodyEl.dom.clientWidth - total_abs;
9544         
9545         var unitWidth = Math.floor(avail / total);
9546         var rem = avail - (unitWidth * total);
9547         
9548         var hidden, width, pos = 0 , splithide , left;
9549         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9550             
9551             hidden = 'display:none;';
9552             left = '';
9553             width  = 'width:0px;';
9554             splithide = '';
9555             if(!cm.isHidden(i)){
9556                 hidden = '';
9557                 
9558                 
9559                 // we can honour xs/sm/md/xl ?
9560                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9561                 if (w===0) {
9562                     hidden = 'display:none;';
9563                 }
9564                 // width should return a small number...
9565                 if (i == last) {
9566                     w+=rem; // add the remaining with..
9567                 }
9568                 pos += w;
9569                 left = "left:" + (pos -4) + "px;";
9570                 width = "width:" + w+ "px;";
9571                 
9572             }
9573             if (this.responsive) {
9574                 width = '';
9575                 left = '';
9576                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9577                 splithide = 'display: none;';
9578             }
9579             
9580             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9581             if (this.headEl) {
9582                 if (i == last) {
9583                     splithide = 'display:none;';
9584                 }
9585                 
9586                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9587                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9588                             // this is the popover version..
9589                             '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9590                 );
9591             }
9592             
9593         }
9594         //Roo.log(styles.join(''));
9595         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9596         
9597     },
9598     
9599     
9600     
9601     onContextMenu : function(e, t)
9602     {
9603         this.processEvent("contextmenu", e);
9604     },
9605     
9606     processEvent : function(name, e)
9607     {
9608         if (name != 'touchstart' ) {
9609             this.fireEvent(name, e);    
9610         }
9611         
9612         var t = e.getTarget();
9613         
9614         var cell = Roo.get(t);
9615         
9616         if(!cell){
9617             return;
9618         }
9619         
9620         if(cell.findParent('tfoot', false, true)){
9621             return;
9622         }
9623         
9624         if(cell.findParent('thead', false, true)){
9625             
9626             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9627                 cell = Roo.get(t).findParent('th', false, true);
9628                 if (!cell) {
9629                     Roo.log("failed to find th in thead?");
9630                     Roo.log(e.getTarget());
9631                     return;
9632                 }
9633             }
9634             
9635             var cellIndex = cell.dom.cellIndex;
9636             
9637             var ename = name == 'touchstart' ? 'click' : name;
9638             this.fireEvent("header" + ename, this, cellIndex, e);
9639             
9640             return;
9641         }
9642         
9643         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9644             cell = Roo.get(t).findParent('td', false, true);
9645             if (!cell) {
9646                 Roo.log("failed to find th in tbody?");
9647                 Roo.log(e.getTarget());
9648                 return;
9649             }
9650         }
9651         
9652         var row = cell.findParent('tr', false, true);
9653         var cellIndex = cell.dom.cellIndex;
9654         var rowIndex = row.dom.rowIndex - 1;
9655         
9656         if(row !== false){
9657             
9658             this.fireEvent("row" + name, this, rowIndex, e);
9659             
9660             if(cell !== false){
9661             
9662                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9663             }
9664         }
9665         
9666     },
9667     
9668     onMouseover : function(e, el)
9669     {
9670         var cell = Roo.get(el);
9671         
9672         if(!cell){
9673             return;
9674         }
9675         
9676         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9677             cell = cell.findParent('td', false, true);
9678         }
9679         
9680         var row = cell.findParent('tr', false, true);
9681         var cellIndex = cell.dom.cellIndex;
9682         var rowIndex = row.dom.rowIndex - 1; // start from 0
9683         
9684         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9685         
9686     },
9687     
9688     onMouseout : function(e, el)
9689     {
9690         var cell = Roo.get(el);
9691         
9692         if(!cell){
9693             return;
9694         }
9695         
9696         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9697             cell = cell.findParent('td', false, true);
9698         }
9699         
9700         var row = cell.findParent('tr', false, true);
9701         var cellIndex = cell.dom.cellIndex;
9702         var rowIndex = row.dom.rowIndex - 1; // start from 0
9703         
9704         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9705         
9706     },
9707     
9708     onClick : function(e, el)
9709     {
9710         var cell = Roo.get(el);
9711         
9712         if(!cell || (!this.cellSelection && !this.rowSelection)){
9713             return;
9714         }
9715         
9716         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9717             cell = cell.findParent('td', false, true);
9718         }
9719         
9720         if(!cell || typeof(cell) == 'undefined'){
9721             return;
9722         }
9723         
9724         var row = cell.findParent('tr', false, true);
9725         
9726         if(!row || typeof(row) == 'undefined'){
9727             return;
9728         }
9729         
9730         var cellIndex = cell.dom.cellIndex;
9731         var rowIndex = this.getRowIndex(row);
9732         
9733         // why??? - should these not be based on SelectionModel?
9734         //if(this.cellSelection){
9735             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9736         //}
9737         
9738         //if(this.rowSelection){
9739             this.fireEvent('rowclick', this, row, rowIndex, e);
9740         //}
9741          
9742     },
9743         
9744     onDblClick : function(e,el)
9745     {
9746         var cell = Roo.get(el);
9747         
9748         if(!cell || (!this.cellSelection && !this.rowSelection)){
9749             return;
9750         }
9751         
9752         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9753             cell = cell.findParent('td', false, true);
9754         }
9755         
9756         if(!cell || typeof(cell) == 'undefined'){
9757             return;
9758         }
9759         
9760         var row = cell.findParent('tr', false, true);
9761         
9762         if(!row || typeof(row) == 'undefined'){
9763             return;
9764         }
9765         
9766         var cellIndex = cell.dom.cellIndex;
9767         var rowIndex = this.getRowIndex(row);
9768         
9769         if(this.cellSelection){
9770             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9771         }
9772         
9773         if(this.rowSelection){
9774             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9775         }
9776     },
9777     findRowIndex : function(el)
9778     {
9779         var cell = Roo.get(el);
9780         if(!cell) {
9781             return false;
9782         }
9783         var row = cell.findParent('tr', false, true);
9784         
9785         if(!row || typeof(row) == 'undefined'){
9786             return false;
9787         }
9788         return this.getRowIndex(row);
9789     },
9790     sort : function(e,el)
9791     {
9792         var col = Roo.get(el);
9793         
9794         if(!col.hasClass('sortable')){
9795             return;
9796         }
9797         
9798         var sort = col.attr('sort');
9799         var dir = 'ASC';
9800         
9801         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9802             dir = 'DESC';
9803         }
9804         
9805         this.store.sortInfo = {field : sort, direction : dir};
9806         
9807         if (this.footer) {
9808             Roo.log("calling footer first");
9809             this.footer.onClick('first');
9810         } else {
9811         
9812             this.store.load({ params : { start : 0 } });
9813         }
9814     },
9815     
9816     renderHeader : function()
9817     {
9818         var header = {
9819             tag: 'thead',
9820             cn : []
9821         };
9822         
9823         var cm = this.cm;
9824         this.totalWidth = 0;
9825         
9826         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9827             
9828             var config = cm.config[i];
9829             
9830             var c = {
9831                 tag: 'th',
9832                 cls : 'x-hcol-' + i,
9833                 style : '',
9834                 
9835                 html: cm.getColumnHeader(i)
9836             };
9837             
9838             var tooltip = cm.getColumnTooltip(i);
9839             if (tooltip) {
9840                 c.tooltip = tooltip;
9841             }
9842             
9843             
9844             var hh = '';
9845             
9846             if(typeof(config.sortable) != 'undefined' && config.sortable){
9847                 c.cls += ' sortable';
9848                 c.html = '<i class="fa"></i>' + c.html;
9849             }
9850             
9851             // could use BS4 hidden-..-down 
9852             
9853             if(typeof(config.lgHeader) != 'undefined'){
9854                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9855             }
9856             
9857             if(typeof(config.mdHeader) != 'undefined'){
9858                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9859             }
9860             
9861             if(typeof(config.smHeader) != 'undefined'){
9862                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9863             }
9864             
9865             if(typeof(config.xsHeader) != 'undefined'){
9866                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9867             }
9868             
9869             if(hh.length){
9870                 c.html = hh;
9871             }
9872             
9873             if(typeof(config.tooltip) != 'undefined'){
9874                 c.tooltip = config.tooltip;
9875             }
9876             
9877             if(typeof(config.colspan) != 'undefined'){
9878                 c.colspan = config.colspan;
9879             }
9880             
9881             // hidden is handled by CSS now
9882             
9883             if(typeof(config.dataIndex) != 'undefined'){
9884                 c.sort = config.dataIndex;
9885             }
9886             
9887            
9888             
9889             if(typeof(config.align) != 'undefined' && config.align.length){
9890                 c.style += ' text-align:' + config.align + ';';
9891             }
9892             
9893             /* width is done in CSS
9894              *if(typeof(config.width) != 'undefined'){
9895                 c.style += ' width:' + config.width + 'px;';
9896                 this.totalWidth += config.width;
9897             } else {
9898                 this.totalWidth += 100; // assume minimum of 100 per column?
9899             }
9900             */
9901             
9902             if(typeof(config.cls) != 'undefined'){
9903                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9904             }
9905             // this is the bit that doesnt reall work at all...
9906             
9907             if (this.responsive) {
9908                  
9909             
9910                 ['xs','sm','md','lg'].map(function(size){
9911                     
9912                     if(typeof(config[size]) == 'undefined'){
9913                         return;
9914                     }
9915                      
9916                     if (!config[size]) { // 0 = hidden
9917                         // BS 4 '0' is treated as hide that column and below.
9918                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9919                         return;
9920                     }
9921                     
9922                     c.cls += ' col-' + size + '-' + config[size] + (
9923                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9924                     );
9925                     
9926                     
9927                 });
9928             }
9929             // at the end?
9930             
9931             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9932             
9933             
9934             
9935             
9936             header.cn.push(c)
9937         }
9938         
9939         return header;
9940     },
9941     
9942     renderBody : function()
9943     {
9944         var body = {
9945             tag: 'tbody',
9946             cn : [
9947                 {
9948                     tag: 'tr',
9949                     cn : [
9950                         {
9951                             tag : 'td',
9952                             colspan :  this.cm.getColumnCount()
9953                         }
9954                     ]
9955                 }
9956             ]
9957         };
9958         
9959         return body;
9960     },
9961     
9962     renderFooter : function()
9963     {
9964         var footer = {
9965             tag: 'tfoot',
9966             cn : [
9967                 {
9968                     tag: 'tr',
9969                     cn : [
9970                         {
9971                             tag : 'td',
9972                             colspan :  this.cm.getColumnCount()
9973                         }
9974                     ]
9975                 }
9976             ]
9977         };
9978         
9979         return footer;
9980     },
9981     
9982     
9983     
9984     onLoad : function()
9985     {
9986 //        Roo.log('ds onload');
9987         this.clear();
9988         
9989         var _this = this;
9990         var cm = this.cm;
9991         var ds = this.store;
9992         
9993         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9994             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9995             if (_this.store.sortInfo) {
9996                     
9997                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9998                     e.select('i', true).addClass(['fa-arrow-up']);
9999                 }
10000                 
10001                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10002                     e.select('i', true).addClass(['fa-arrow-down']);
10003                 }
10004             }
10005         });
10006         
10007         var tbody =  this.bodyEl;
10008               
10009         if(ds.getCount() > 0){
10010             ds.data.each(function(d,rowIndex){
10011                 var row =  this.renderRow(cm, ds, rowIndex);
10012                 
10013                 tbody.createChild(row);
10014                 
10015                 var _this = this;
10016                 
10017                 if(row.cellObjects.length){
10018                     Roo.each(row.cellObjects, function(r){
10019                         _this.renderCellObject(r);
10020                     })
10021                 }
10022                 
10023             }, this);
10024         } else if (this.empty_results.length) {
10025             this.el.mask(this.empty_results, 'no-spinner');
10026         }
10027         
10028         var tfoot = this.el.select('tfoot', true).first();
10029         
10030         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10031             
10032             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10033             
10034             var total = this.ds.getTotalCount();
10035             
10036             if(this.footer.pageSize < total){
10037                 this.mainFoot.show();
10038             }
10039         }
10040         
10041         Roo.each(this.el.select('tbody td', true).elements, function(e){
10042             e.on('mouseover', _this.onMouseover, _this);
10043         });
10044         
10045         Roo.each(this.el.select('tbody td', true).elements, function(e){
10046             e.on('mouseout', _this.onMouseout, _this);
10047         });
10048         this.fireEvent('rowsrendered', this);
10049         
10050         this.autoSize();
10051         
10052         this.initCSS(); /// resize cols
10053
10054         
10055     },
10056     
10057     
10058     onUpdate : function(ds,record)
10059     {
10060         this.refreshRow(record);
10061         this.autoSize();
10062     },
10063     
10064     onRemove : function(ds, record, index, isUpdate){
10065         if(isUpdate !== true){
10066             this.fireEvent("beforerowremoved", this, index, record);
10067         }
10068         var bt = this.bodyEl.dom;
10069         
10070         var rows = this.el.select('tbody > tr', true).elements;
10071         
10072         if(typeof(rows[index]) != 'undefined'){
10073             bt.removeChild(rows[index].dom);
10074         }
10075         
10076 //        if(bt.rows[index]){
10077 //            bt.removeChild(bt.rows[index]);
10078 //        }
10079         
10080         if(isUpdate !== true){
10081             //this.stripeRows(index);
10082             //this.syncRowHeights(index, index);
10083             //this.layout();
10084             this.fireEvent("rowremoved", this, index, record);
10085         }
10086     },
10087     
10088     onAdd : function(ds, records, rowIndex)
10089     {
10090         //Roo.log('on Add called');
10091         // - note this does not handle multiple adding very well..
10092         var bt = this.bodyEl.dom;
10093         for (var i =0 ; i < records.length;i++) {
10094             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10095             //Roo.log(records[i]);
10096             //Roo.log(this.store.getAt(rowIndex+i));
10097             this.insertRow(this.store, rowIndex + i, false);
10098             return;
10099         }
10100         
10101     },
10102     
10103     
10104     refreshRow : function(record){
10105         var ds = this.store, index;
10106         if(typeof record == 'number'){
10107             index = record;
10108             record = ds.getAt(index);
10109         }else{
10110             index = ds.indexOf(record);
10111             if (index < 0) {
10112                 return; // should not happen - but seems to 
10113             }
10114         }
10115         this.insertRow(ds, index, true);
10116         this.autoSize();
10117         this.onRemove(ds, record, index+1, true);
10118         this.autoSize();
10119         //this.syncRowHeights(index, index);
10120         //this.layout();
10121         this.fireEvent("rowupdated", this, index, record);
10122     },
10123     // private - called by RowSelection
10124     onRowSelect : function(rowIndex){
10125         var row = this.getRowDom(rowIndex);
10126         row.addClass(['bg-info','info']);
10127     },
10128     // private - called by RowSelection
10129     onRowDeselect : function(rowIndex)
10130     {
10131         if (rowIndex < 0) {
10132             return;
10133         }
10134         var row = this.getRowDom(rowIndex);
10135         row.removeClass(['bg-info','info']);
10136     },
10137       /**
10138      * Focuses the specified row.
10139      * @param {Number} row The row index
10140      */
10141     focusRow : function(row)
10142     {
10143         //Roo.log('GridView.focusRow');
10144         var x = this.bodyEl.dom.scrollLeft;
10145         this.focusCell(row, 0, false);
10146         this.bodyEl.dom.scrollLeft = x;
10147
10148     },
10149      /**
10150      * Focuses the specified cell.
10151      * @param {Number} row The row index
10152      * @param {Number} col The column index
10153      * @param {Boolean} hscroll false to disable horizontal scrolling
10154      */
10155     focusCell : function(row, col, hscroll)
10156     {
10157         //Roo.log('GridView.focusCell');
10158         var el = this.ensureVisible(row, col, hscroll);
10159         // not sure what focusEL achives = it's a <a> pos relative 
10160         //this.focusEl.alignTo(el, "tl-tl");
10161         //if(Roo.isGecko){
10162         //    this.focusEl.focus();
10163         //}else{
10164         //    this.focusEl.focus.defer(1, this.focusEl);
10165         //}
10166     },
10167     
10168      /**
10169      * Scrolls the specified cell into view
10170      * @param {Number} row The row index
10171      * @param {Number} col The column index
10172      * @param {Boolean} hscroll false to disable horizontal scrolling
10173      */
10174     ensureVisible : function(row, col, hscroll)
10175     {
10176         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10177         //return null; //disable for testing.
10178         if(typeof row != "number"){
10179             row = row.rowIndex;
10180         }
10181         if(row < 0 && row >= this.ds.getCount()){
10182             return  null;
10183         }
10184         col = (col !== undefined ? col : 0);
10185         var cm = this.cm;
10186         while(cm.isHidden(col)){
10187             col++;
10188         }
10189
10190         var el = this.getCellDom(row, col);
10191         if(!el){
10192             return null;
10193         }
10194         var c = this.bodyEl.dom;
10195
10196         var ctop = parseInt(el.offsetTop, 10);
10197         var cleft = parseInt(el.offsetLeft, 10);
10198         var cbot = ctop + el.offsetHeight;
10199         var cright = cleft + el.offsetWidth;
10200
10201         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10202         var ch = 0; //?? header is not withing the area?
10203         var stop = parseInt(c.scrollTop, 10);
10204         var sleft = parseInt(c.scrollLeft, 10);
10205         var sbot = stop + ch;
10206         var sright = sleft + c.clientWidth;
10207         /*
10208         Roo.log('GridView.ensureVisible:' +
10209                 ' ctop:' + ctop +
10210                 ' c.clientHeight:' + c.clientHeight +
10211                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10212                 ' stop:' + stop +
10213                 ' cbot:' + cbot +
10214                 ' sbot:' + sbot +
10215                 ' ch:' + ch  
10216                 );
10217         */
10218         if(ctop < stop){
10219             c.scrollTop = ctop;
10220             //Roo.log("set scrolltop to ctop DISABLE?");
10221         }else if(cbot > sbot){
10222             //Roo.log("set scrolltop to cbot-ch");
10223             c.scrollTop = cbot-ch;
10224         }
10225
10226         if(hscroll !== false){
10227             if(cleft < sleft){
10228                 c.scrollLeft = cleft;
10229             }else if(cright > sright){
10230                 c.scrollLeft = cright-c.clientWidth;
10231             }
10232         }
10233
10234         return el;
10235     },
10236     
10237     
10238     insertRow : function(dm, rowIndex, isUpdate){
10239         
10240         if(!isUpdate){
10241             this.fireEvent("beforerowsinserted", this, rowIndex);
10242         }
10243             //var s = this.getScrollState();
10244         var row = this.renderRow(this.cm, this.store, rowIndex);
10245         // insert before rowIndex..
10246         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10247         
10248         var _this = this;
10249                 
10250         if(row.cellObjects.length){
10251             Roo.each(row.cellObjects, function(r){
10252                 _this.renderCellObject(r);
10253             })
10254         }
10255             
10256         if(!isUpdate){
10257             this.fireEvent("rowsinserted", this, rowIndex);
10258             //this.syncRowHeights(firstRow, lastRow);
10259             //this.stripeRows(firstRow);
10260             //this.layout();
10261         }
10262         
10263     },
10264     
10265     
10266     getRowDom : function(rowIndex)
10267     {
10268         var rows = this.el.select('tbody > tr', true).elements;
10269         
10270         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10271         
10272     },
10273     getCellDom : function(rowIndex, colIndex)
10274     {
10275         var row = this.getRowDom(rowIndex);
10276         if (row === false) {
10277             return false;
10278         }
10279         var cols = row.select('td', true).elements;
10280         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10281         
10282     },
10283     
10284     // returns the object tree for a tr..
10285   
10286     
10287     renderRow : function(cm, ds, rowIndex) 
10288     {
10289         var d = ds.getAt(rowIndex);
10290         
10291         var row = {
10292             tag : 'tr',
10293             cls : 'x-row-' + rowIndex,
10294             cn : []
10295         };
10296             
10297         var cellObjects = [];
10298         
10299         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10300             var config = cm.config[i];
10301             
10302             var renderer = cm.getRenderer(i);
10303             var value = '';
10304             var id = false;
10305             
10306             if(typeof(renderer) !== 'undefined'){
10307                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10308             }
10309             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10310             // and are rendered into the cells after the row is rendered - using the id for the element.
10311             
10312             if(typeof(value) === 'object'){
10313                 id = Roo.id();
10314                 cellObjects.push({
10315                     container : id,
10316                     cfg : value 
10317                 })
10318             }
10319             
10320             var rowcfg = {
10321                 record: d,
10322                 rowIndex : rowIndex,
10323                 colIndex : i,
10324                 rowClass : ''
10325             };
10326
10327             this.fireEvent('rowclass', this, rowcfg);
10328             
10329             var td = {
10330                 tag: 'td',
10331                 // this might end up displaying HTML?
10332                 // this is too messy... - better to only do it on columsn you know are going to be too long
10333                 //tooltip : (typeof(value) === 'object') ? '' : value,
10334                 cls : rowcfg.rowClass + ' x-col-' + i,
10335                 style: '',
10336                 html: (typeof(value) === 'object') ? '' : value
10337             };
10338             
10339             if (id) {
10340                 td.id = id;
10341             }
10342             
10343             if(typeof(config.colspan) != 'undefined'){
10344                 td.colspan = config.colspan;
10345             }
10346             
10347             
10348             
10349             if(typeof(config.align) != 'undefined' && config.align.length){
10350                 td.style += ' text-align:' + config.align + ';';
10351             }
10352             if(typeof(config.valign) != 'undefined' && config.valign.length){
10353                 td.style += ' vertical-align:' + config.valign + ';';
10354             }
10355             /*
10356             if(typeof(config.width) != 'undefined'){
10357                 td.style += ' width:' +  config.width + 'px;';
10358             }
10359             */
10360             
10361             if(typeof(config.cursor) != 'undefined'){
10362                 td.style += ' cursor:' +  config.cursor + ';';
10363             }
10364             
10365             if(typeof(config.cls) != 'undefined'){
10366                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10367             }
10368             if (this.responsive) {
10369                 ['xs','sm','md','lg'].map(function(size){
10370                     
10371                     if(typeof(config[size]) == 'undefined'){
10372                         return;
10373                     }
10374                     
10375                     
10376                       
10377                     if (!config[size]) { // 0 = hidden
10378                         // BS 4 '0' is treated as hide that column and below.
10379                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10380                         return;
10381                     }
10382                     
10383                     td.cls += ' col-' + size + '-' + config[size] + (
10384                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
10385                     );
10386                      
10387     
10388                 });
10389             }
10390             row.cn.push(td);
10391            
10392         }
10393         
10394         row.cellObjects = cellObjects;
10395         
10396         return row;
10397           
10398     },
10399     
10400     
10401     
10402     onBeforeLoad : function()
10403     {
10404         this.el.unmask(); // if needed.
10405     },
10406      /**
10407      * Remove all rows
10408      */
10409     clear : function()
10410     {
10411         this.el.select('tbody', true).first().dom.innerHTML = '';
10412     },
10413     /**
10414      * Show or hide a row.
10415      * @param {Number} rowIndex to show or hide
10416      * @param {Boolean} state hide
10417      */
10418     setRowVisibility : function(rowIndex, state)
10419     {
10420         var bt = this.bodyEl.dom;
10421         
10422         var rows = this.el.select('tbody > tr', true).elements;
10423         
10424         if(typeof(rows[rowIndex]) == 'undefined'){
10425             return;
10426         }
10427         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10428         
10429     },
10430     
10431     
10432     getSelectionModel : function(){
10433         if(!this.selModel){
10434             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10435         }
10436         return this.selModel;
10437     },
10438     /*
10439      * Render the Roo.bootstrap object from renderder
10440      */
10441     renderCellObject : function(r)
10442     {
10443         var _this = this;
10444         
10445         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10446         
10447         var t = r.cfg.render(r.container);
10448         
10449         if(r.cfg.cn){
10450             Roo.each(r.cfg.cn, function(c){
10451                 var child = {
10452                     container: t.getChildContainer(),
10453                     cfg: c
10454                 };
10455                 _this.renderCellObject(child);
10456             })
10457         }
10458     },
10459     /**
10460      * get the Row Index from a dom element.
10461      * @param {Roo.Element} row The row to look for
10462      * @returns {Number} the row
10463      */
10464     getRowIndex : function(row)
10465     {
10466         var rowIndex = -1;
10467         
10468         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10469             if(el != row){
10470                 return;
10471             }
10472             
10473             rowIndex = index;
10474         });
10475         
10476         return rowIndex;
10477     },
10478     /**
10479      * get the header TH element for columnIndex
10480      * @param {Number} columnIndex
10481      * @returns {Roo.Element}
10482      */
10483     getHeaderIndex: function(colIndex)
10484     {
10485         var cols = this.headEl.select('th', true).elements;
10486         return cols[colIndex]; 
10487     },
10488     /**
10489      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10490      * @param {domElement} cell to look for
10491      * @returns {Number} the column
10492      */
10493     getCellIndex : function(cell)
10494     {
10495         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10496         if(id){
10497             return parseInt(id[1], 10);
10498         }
10499         return 0;
10500     },
10501      /**
10502      * Returns the grid's underlying element = used by panel.Grid
10503      * @return {Element} The element
10504      */
10505     getGridEl : function(){
10506         return this.el;
10507     },
10508      /**
10509      * Forces a resize - used by panel.Grid
10510      * @return {Element} The element
10511      */
10512     autoSize : function()
10513     {
10514         if(this.disableAutoSize) {
10515             return;
10516         }
10517         //var ctr = Roo.get(this.container.dom.parentElement);
10518         var ctr = Roo.get(this.el.dom);
10519         
10520         var thd = this.getGridEl().select('thead',true).first();
10521         var tbd = this.getGridEl().select('tbody', true).first();
10522         var tfd = this.getGridEl().select('tfoot', true).first();
10523         
10524         var cw = ctr.getWidth();
10525         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10526         
10527         if (tbd) {
10528             
10529             tbd.setWidth(ctr.getWidth());
10530             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10531             // this needs fixing for various usage - currently only hydra job advers I think..
10532             //tdb.setHeight(
10533             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10534             //); 
10535             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10536             cw -= barsize;
10537         }
10538         cw = Math.max(cw, this.totalWidth);
10539         this.getGridEl().select('tbody tr',true).setWidth(cw);
10540         this.initCSS();
10541         
10542         // resize 'expandable coloumn?
10543         
10544         return; // we doe not have a view in this design..
10545         
10546     },
10547     onBodyScroll: function()
10548     {
10549         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10550         if(this.headEl){
10551             this.headEl.setStyle({
10552                 'position' : 'relative',
10553                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10554             });
10555         }
10556         
10557         if(this.lazyLoad){
10558             
10559             var scrollHeight = this.bodyEl.dom.scrollHeight;
10560             
10561             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10562             
10563             var height = this.bodyEl.getHeight();
10564             
10565             if(scrollHeight - height == scrollTop) {
10566                 
10567                 var total = this.ds.getTotalCount();
10568                 
10569                 if(this.footer.cursor + this.footer.pageSize < total){
10570                     
10571                     this.footer.ds.load({
10572                         params : {
10573                             start : this.footer.cursor + this.footer.pageSize,
10574                             limit : this.footer.pageSize
10575                         },
10576                         add : true
10577                     });
10578                 }
10579             }
10580             
10581         }
10582     },
10583     onColumnSplitterMoved : function(i, diff)
10584     {
10585         this.userResized = true;
10586         
10587         var cm = this.colModel;
10588         
10589         var w = this.getHeaderIndex(i).getWidth() + diff;
10590         
10591         
10592         cm.setColumnWidth(i, w, true);
10593         this.initCSS();
10594         //var cid = cm.getColumnId(i); << not used in this version?
10595        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10596         
10597         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10598         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10599         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10600 */
10601         //this.updateSplitters();
10602         //this.layout(); << ??
10603         this.fireEvent("columnresize", i, w);
10604     },
10605     onHeaderChange : function()
10606     {
10607         var header = this.renderHeader();
10608         var table = this.el.select('table', true).first();
10609         
10610         this.headEl.remove();
10611         this.headEl = table.createChild(header, this.bodyEl, false);
10612         
10613         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10614             e.on('click', this.sort, this);
10615         }, this);
10616         
10617         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10618             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10619         }
10620         
10621     },
10622     
10623     onHiddenChange : function(colModel, colIndex, hidden)
10624     {
10625         /*
10626         this.cm.setHidden()
10627         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10628         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10629         
10630         this.CSS.updateRule(thSelector, "display", "");
10631         this.CSS.updateRule(tdSelector, "display", "");
10632         
10633         if(hidden){
10634             this.CSS.updateRule(thSelector, "display", "none");
10635             this.CSS.updateRule(tdSelector, "display", "none");
10636         }
10637         */
10638         // onload calls initCSS()
10639         this.onHeaderChange();
10640         this.onLoad();
10641     },
10642     
10643     setColumnWidth: function(col_index, width)
10644     {
10645         // width = "md-2 xs-2..."
10646         if(!this.colModel.config[col_index]) {
10647             return;
10648         }
10649         
10650         var w = width.split(" ");
10651         
10652         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10653         
10654         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10655         
10656         
10657         for(var j = 0; j < w.length; j++) {
10658             
10659             if(!w[j]) {
10660                 continue;
10661             }
10662             
10663             var size_cls = w[j].split("-");
10664             
10665             if(!Number.isInteger(size_cls[1] * 1)) {
10666                 continue;
10667             }
10668             
10669             if(!this.colModel.config[col_index][size_cls[0]]) {
10670                 continue;
10671             }
10672             
10673             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10674                 continue;
10675             }
10676             
10677             h_row[0].classList.replace(
10678                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10679                 "col-"+size_cls[0]+"-"+size_cls[1]
10680             );
10681             
10682             for(var i = 0; i < rows.length; i++) {
10683                 
10684                 var size_cls = w[j].split("-");
10685                 
10686                 if(!Number.isInteger(size_cls[1] * 1)) {
10687                     continue;
10688                 }
10689                 
10690                 if(!this.colModel.config[col_index][size_cls[0]]) {
10691                     continue;
10692                 }
10693                 
10694                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10695                     continue;
10696                 }
10697                 
10698                 rows[i].classList.replace(
10699                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10700                     "col-"+size_cls[0]+"-"+size_cls[1]
10701                 );
10702             }
10703             
10704             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10705         }
10706     }
10707 });
10708
10709 // currently only used to find the split on drag.. 
10710 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10711
10712 /**
10713  * @depricated
10714 */
10715 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10716 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10717 /*
10718  * - LGPL
10719  *
10720  * table cell
10721  * 
10722  */
10723
10724 /**
10725  * @class Roo.bootstrap.TableCell
10726  * @extends Roo.bootstrap.Component
10727  * @children Roo.bootstrap.Component
10728  * @parent Roo.bootstrap.TableRow
10729  * Bootstrap TableCell class
10730  * 
10731  * @cfg {String} html cell contain text
10732  * @cfg {String} cls cell class
10733  * @cfg {String} tag cell tag (td|th) default td
10734  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10735  * @cfg {String} align Aligns the content in a cell
10736  * @cfg {String} axis Categorizes cells
10737  * @cfg {String} bgcolor Specifies the background color of a cell
10738  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10739  * @cfg {Number} colspan Specifies the number of columns a cell should span
10740  * @cfg {String} headers Specifies one or more header cells a cell is related to
10741  * @cfg {Number} height Sets the height of a cell
10742  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10743  * @cfg {Number} rowspan Sets the number of rows a cell should span
10744  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10745  * @cfg {String} valign Vertical aligns the content in a cell
10746  * @cfg {Number} width Specifies the width of a cell
10747  * 
10748  * @constructor
10749  * Create a new TableCell
10750  * @param {Object} config The config object
10751  */
10752
10753 Roo.bootstrap.TableCell = function(config){
10754     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10755 };
10756
10757 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10758     
10759     html: false,
10760     cls: false,
10761     tag: false,
10762     abbr: false,
10763     align: false,
10764     axis: false,
10765     bgcolor: false,
10766     charoff: false,
10767     colspan: false,
10768     headers: false,
10769     height: false,
10770     nowrap: false,
10771     rowspan: false,
10772     scope: false,
10773     valign: false,
10774     width: false,
10775     
10776     
10777     getAutoCreate : function(){
10778         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10779         
10780         cfg = {
10781             tag: 'td'
10782         };
10783         
10784         if(this.tag){
10785             cfg.tag = this.tag;
10786         }
10787         
10788         if (this.html) {
10789             cfg.html=this.html
10790         }
10791         if (this.cls) {
10792             cfg.cls=this.cls
10793         }
10794         if (this.abbr) {
10795             cfg.abbr=this.abbr
10796         }
10797         if (this.align) {
10798             cfg.align=this.align
10799         }
10800         if (this.axis) {
10801             cfg.axis=this.axis
10802         }
10803         if (this.bgcolor) {
10804             cfg.bgcolor=this.bgcolor
10805         }
10806         if (this.charoff) {
10807             cfg.charoff=this.charoff
10808         }
10809         if (this.colspan) {
10810             cfg.colspan=this.colspan
10811         }
10812         if (this.headers) {
10813             cfg.headers=this.headers
10814         }
10815         if (this.height) {
10816             cfg.height=this.height
10817         }
10818         if (this.nowrap) {
10819             cfg.nowrap=this.nowrap
10820         }
10821         if (this.rowspan) {
10822             cfg.rowspan=this.rowspan
10823         }
10824         if (this.scope) {
10825             cfg.scope=this.scope
10826         }
10827         if (this.valign) {
10828             cfg.valign=this.valign
10829         }
10830         if (this.width) {
10831             cfg.width=this.width
10832         }
10833         
10834         
10835         return cfg;
10836     }
10837    
10838 });
10839
10840  
10841
10842  /*
10843  * - LGPL
10844  *
10845  * table row
10846  * 
10847  */
10848
10849 /**
10850  * @class Roo.bootstrap.TableRow
10851  * @extends Roo.bootstrap.Component
10852  * @children Roo.bootstrap.TableCell
10853  * @parent Roo.bootstrap.TableBody
10854  * Bootstrap TableRow class
10855  * @cfg {String} cls row class
10856  * @cfg {String} align Aligns the content in a table row
10857  * @cfg {String} bgcolor Specifies a background color for a table row
10858  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10859  * @cfg {String} valign Vertical aligns the content in a table row
10860  * 
10861  * @constructor
10862  * Create a new TableRow
10863  * @param {Object} config The config object
10864  */
10865
10866 Roo.bootstrap.TableRow = function(config){
10867     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10868 };
10869
10870 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10871     
10872     cls: false,
10873     align: false,
10874     bgcolor: false,
10875     charoff: false,
10876     valign: false,
10877     
10878     getAutoCreate : function(){
10879         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10880         
10881         cfg = {
10882             tag: 'tr'
10883         };
10884             
10885         if(this.cls){
10886             cfg.cls = this.cls;
10887         }
10888         if(this.align){
10889             cfg.align = this.align;
10890         }
10891         if(this.bgcolor){
10892             cfg.bgcolor = this.bgcolor;
10893         }
10894         if(this.charoff){
10895             cfg.charoff = this.charoff;
10896         }
10897         if(this.valign){
10898             cfg.valign = this.valign;
10899         }
10900         
10901         return cfg;
10902     }
10903    
10904 });
10905
10906  
10907
10908  /*
10909  * - LGPL
10910  *
10911  * table body
10912  * 
10913  */
10914
10915 /**
10916  * @class Roo.bootstrap.TableBody
10917  * @extends Roo.bootstrap.Component
10918  * @children Roo.bootstrap.TableRow
10919  * @parent Roo.bootstrap.Table
10920  * Bootstrap TableBody class
10921  * @cfg {String} cls element class
10922  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10923  * @cfg {String} align Aligns the content inside the element
10924  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10925  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10926  * 
10927  * @constructor
10928  * Create a new TableBody
10929  * @param {Object} config The config object
10930  */
10931
10932 Roo.bootstrap.TableBody = function(config){
10933     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10934 };
10935
10936 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10937     
10938     cls: false,
10939     tag: false,
10940     align: false,
10941     charoff: false,
10942     valign: false,
10943     
10944     getAutoCreate : function(){
10945         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10946         
10947         cfg = {
10948             tag: 'tbody'
10949         };
10950             
10951         if (this.cls) {
10952             cfg.cls=this.cls
10953         }
10954         if(this.tag){
10955             cfg.tag = this.tag;
10956         }
10957         
10958         if(this.align){
10959             cfg.align = this.align;
10960         }
10961         if(this.charoff){
10962             cfg.charoff = this.charoff;
10963         }
10964         if(this.valign){
10965             cfg.valign = this.valign;
10966         }
10967         
10968         return cfg;
10969     }
10970     
10971     
10972 //    initEvents : function()
10973 //    {
10974 //        
10975 //        if(!this.store){
10976 //            return;
10977 //        }
10978 //        
10979 //        this.store = Roo.factory(this.store, Roo.data);
10980 //        this.store.on('load', this.onLoad, this);
10981 //        
10982 //        this.store.load();
10983 //        
10984 //    },
10985 //    
10986 //    onLoad: function () 
10987 //    {   
10988 //        this.fireEvent('load', this);
10989 //    }
10990 //    
10991 //   
10992 });
10993
10994  
10995
10996  /*
10997  * Based on:
10998  * Ext JS Library 1.1.1
10999  * Copyright(c) 2006-2007, Ext JS, LLC.
11000  *
11001  * Originally Released Under LGPL - original licence link has changed is not relivant.
11002  *
11003  * Fork - LGPL
11004  * <script type="text/javascript">
11005  */
11006
11007 // as we use this in bootstrap.
11008 Roo.namespace('Roo.form');
11009  /**
11010  * @class Roo.form.Action
11011  * Internal Class used to handle form actions
11012  * @constructor
11013  * @param {Roo.form.BasicForm} el The form element or its id
11014  * @param {Object} config Configuration options
11015  */
11016
11017  
11018  
11019 // define the action interface
11020 Roo.form.Action = function(form, options){
11021     this.form = form;
11022     this.options = options || {};
11023 };
11024 /**
11025  * Client Validation Failed
11026  * @const 
11027  */
11028 Roo.form.Action.CLIENT_INVALID = 'client';
11029 /**
11030  * Server Validation Failed
11031  * @const 
11032  */
11033 Roo.form.Action.SERVER_INVALID = 'server';
11034  /**
11035  * Connect to Server Failed
11036  * @const 
11037  */
11038 Roo.form.Action.CONNECT_FAILURE = 'connect';
11039 /**
11040  * Reading Data from Server Failed
11041  * @const 
11042  */
11043 Roo.form.Action.LOAD_FAILURE = 'load';
11044
11045 Roo.form.Action.prototype = {
11046     type : 'default',
11047     failureType : undefined,
11048     response : undefined,
11049     result : undefined,
11050
11051     // interface method
11052     run : function(options){
11053
11054     },
11055
11056     // interface method
11057     success : function(response){
11058
11059     },
11060
11061     // interface method
11062     handleResponse : function(response){
11063
11064     },
11065
11066     // default connection failure
11067     failure : function(response){
11068         
11069         this.response = response;
11070         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11071         this.form.afterAction(this, false);
11072     },
11073
11074     processResponse : function(response){
11075         this.response = response;
11076         if(!response.responseText){
11077             return true;
11078         }
11079         this.result = this.handleResponse(response);
11080         return this.result;
11081     },
11082
11083     // utility functions used internally
11084     getUrl : function(appendParams){
11085         var url = this.options.url || this.form.url || this.form.el.dom.action;
11086         if(appendParams){
11087             var p = this.getParams();
11088             if(p){
11089                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11090             }
11091         }
11092         return url;
11093     },
11094
11095     getMethod : function(){
11096         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11097     },
11098
11099     getParams : function(){
11100         var bp = this.form.baseParams;
11101         var p = this.options.params;
11102         if(p){
11103             if(typeof p == "object"){
11104                 p = Roo.urlEncode(Roo.applyIf(p, bp));
11105             }else if(typeof p == 'string' && bp){
11106                 p += '&' + Roo.urlEncode(bp);
11107             }
11108         }else if(bp){
11109             p = Roo.urlEncode(bp);
11110         }
11111         return p;
11112     },
11113
11114     createCallback : function(){
11115         return {
11116             success: this.success,
11117             failure: this.failure,
11118             scope: this,
11119             timeout: (this.form.timeout*1000),
11120             upload: this.form.fileUpload ? this.success : undefined
11121         };
11122     }
11123 };
11124
11125 Roo.form.Action.Submit = function(form, options){
11126     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11127 };
11128
11129 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11130     type : 'submit',
11131
11132     haveProgress : false,
11133     uploadComplete : false,
11134     
11135     // uploadProgress indicator.
11136     uploadProgress : function()
11137     {
11138         if (!this.form.progressUrl) {
11139             return;
11140         }
11141         
11142         if (!this.haveProgress) {
11143             Roo.MessageBox.progress("Uploading", "Uploading");
11144         }
11145         if (this.uploadComplete) {
11146            Roo.MessageBox.hide();
11147            return;
11148         }
11149         
11150         this.haveProgress = true;
11151    
11152         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11153         
11154         var c = new Roo.data.Connection();
11155         c.request({
11156             url : this.form.progressUrl,
11157             params: {
11158                 id : uid
11159             },
11160             method: 'GET',
11161             success : function(req){
11162                //console.log(data);
11163                 var rdata = false;
11164                 var edata;
11165                 try  {
11166                    rdata = Roo.decode(req.responseText)
11167                 } catch (e) {
11168                     Roo.log("Invalid data from server..");
11169                     Roo.log(edata);
11170                     return;
11171                 }
11172                 if (!rdata || !rdata.success) {
11173                     Roo.log(rdata);
11174                     Roo.MessageBox.alert(Roo.encode(rdata));
11175                     return;
11176                 }
11177                 var data = rdata.data;
11178                 
11179                 if (this.uploadComplete) {
11180                    Roo.MessageBox.hide();
11181                    return;
11182                 }
11183                    
11184                 if (data){
11185                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11186                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11187                     );
11188                 }
11189                 this.uploadProgress.defer(2000,this);
11190             },
11191        
11192             failure: function(data) {
11193                 Roo.log('progress url failed ');
11194                 Roo.log(data);
11195             },
11196             scope : this
11197         });
11198            
11199     },
11200     
11201     
11202     run : function()
11203     {
11204         // run get Values on the form, so it syncs any secondary forms.
11205         this.form.getValues();
11206         
11207         var o = this.options;
11208         var method = this.getMethod();
11209         var isPost = method == 'POST';
11210         if(o.clientValidation === false || this.form.isValid()){
11211             
11212             if (this.form.progressUrl) {
11213                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11214                     (new Date() * 1) + '' + Math.random());
11215                     
11216             } 
11217             
11218             
11219             Roo.Ajax.request(Roo.apply(this.createCallback(), {
11220                 form:this.form.el.dom,
11221                 url:this.getUrl(!isPost),
11222                 method: method,
11223                 params:isPost ? this.getParams() : null,
11224                 isUpload: this.form.fileUpload,
11225                 formData : this.form.formData
11226             }));
11227             
11228             this.uploadProgress();
11229
11230         }else if (o.clientValidation !== false){ // client validation failed
11231             this.failureType = Roo.form.Action.CLIENT_INVALID;
11232             this.form.afterAction(this, false);
11233         }
11234     },
11235
11236     success : function(response)
11237     {
11238         this.uploadComplete= true;
11239         if (this.haveProgress) {
11240             Roo.MessageBox.hide();
11241         }
11242         
11243         
11244         var result = this.processResponse(response);
11245         if(result === true || result.success){
11246             this.form.afterAction(this, true);
11247             return;
11248         }
11249         if(result.errors){
11250             this.form.markInvalid(result.errors);
11251             this.failureType = Roo.form.Action.SERVER_INVALID;
11252         }
11253         this.form.afterAction(this, false);
11254     },
11255     failure : function(response)
11256     {
11257         this.uploadComplete= true;
11258         if (this.haveProgress) {
11259             Roo.MessageBox.hide();
11260         }
11261         
11262         this.response = response;
11263         this.failureType = Roo.form.Action.CONNECT_FAILURE;
11264         this.form.afterAction(this, false);
11265     },
11266     
11267     handleResponse : function(response){
11268         if(this.form.errorReader){
11269             var rs = this.form.errorReader.read(response);
11270             var errors = [];
11271             if(rs.records){
11272                 for(var i = 0, len = rs.records.length; i < len; i++) {
11273                     var r = rs.records[i];
11274                     errors[i] = r.data;
11275                 }
11276             }
11277             if(errors.length < 1){
11278                 errors = null;
11279             }
11280             return {
11281                 success : rs.success,
11282                 errors : errors
11283             };
11284         }
11285         var ret = false;
11286         try {
11287             ret = Roo.decode(response.responseText);
11288         } catch (e) {
11289             ret = {
11290                 success: false,
11291                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11292                 errors : []
11293             };
11294         }
11295         return ret;
11296         
11297     }
11298 });
11299
11300
11301 Roo.form.Action.Load = function(form, options){
11302     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11303     this.reader = this.form.reader;
11304 };
11305
11306 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11307     type : 'load',
11308
11309     run : function(){
11310         
11311         Roo.Ajax.request(Roo.apply(
11312                 this.createCallback(), {
11313                     method:this.getMethod(),
11314                     url:this.getUrl(false),
11315                     params:this.getParams()
11316         }));
11317     },
11318
11319     success : function(response){
11320         
11321         var result = this.processResponse(response);
11322         if(result === true || !result.success || !result.data){
11323             this.failureType = Roo.form.Action.LOAD_FAILURE;
11324             this.form.afterAction(this, false);
11325             return;
11326         }
11327         this.form.clearInvalid();
11328         this.form.setValues(result.data);
11329         this.form.afterAction(this, true);
11330     },
11331
11332     handleResponse : function(response){
11333         if(this.form.reader){
11334             var rs = this.form.reader.read(response);
11335             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11336             return {
11337                 success : rs.success,
11338                 data : data
11339             };
11340         }
11341         return Roo.decode(response.responseText);
11342     }
11343 });
11344
11345 Roo.form.Action.ACTION_TYPES = {
11346     'load' : Roo.form.Action.Load,
11347     'submit' : Roo.form.Action.Submit
11348 };/*
11349  * - LGPL
11350  *
11351  * form
11352  *
11353  */
11354
11355 /**
11356  * @class Roo.bootstrap.form.Form
11357  * @extends Roo.bootstrap.Component
11358  * @children Roo.bootstrap.Component
11359  * Bootstrap Form class
11360  * @cfg {String} method  GET | POST (default POST)
11361  * @cfg {String} labelAlign top | left (default top)
11362  * @cfg {String} align left  | right - for navbars
11363  * @cfg {Boolean} loadMask load mask when submit (default true)
11364
11365  *
11366  * @constructor
11367  * Create a new Form
11368  * @param {Object} config The config object
11369  */
11370
11371
11372 Roo.bootstrap.form.Form = function(config){
11373     
11374     Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11375     
11376     Roo.bootstrap.form.Form.popover.apply();
11377     
11378     this.addEvents({
11379         /**
11380          * @event clientvalidation
11381          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11382          * @param {Form} this
11383          * @param {Boolean} valid true if the form has passed client-side validation
11384          */
11385         clientvalidation: true,
11386         /**
11387          * @event beforeaction
11388          * Fires before any action is performed. Return false to cancel the action.
11389          * @param {Form} this
11390          * @param {Action} action The action to be performed
11391          */
11392         beforeaction: true,
11393         /**
11394          * @event actionfailed
11395          * Fires when an action fails.
11396          * @param {Form} this
11397          * @param {Action} action The action that failed
11398          */
11399         actionfailed : true,
11400         /**
11401          * @event actioncomplete
11402          * Fires when an action is completed.
11403          * @param {Form} this
11404          * @param {Action} action The action that completed
11405          */
11406         actioncomplete : true
11407     });
11408 };
11409
11410 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component,  {
11411
11412      /**
11413      * @cfg {String} method
11414      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11415      */
11416     method : 'POST',
11417     /**
11418      * @cfg {String} url
11419      * The URL to use for form actions if one isn't supplied in the action options.
11420      */
11421     /**
11422      * @cfg {Boolean} fileUpload
11423      * Set to true if this form is a file upload.
11424      */
11425
11426     /**
11427      * @cfg {Object} baseParams
11428      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11429      */
11430
11431     /**
11432      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11433      */
11434     timeout: 30,
11435     /**
11436      * @cfg {Sting} align (left|right) for navbar forms
11437      */
11438     align : 'left',
11439
11440     // private
11441     activeAction : null,
11442
11443     /**
11444      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11445      * element by passing it or its id or mask the form itself by passing in true.
11446      * @type Mixed
11447      */
11448     waitMsgTarget : false,
11449
11450     loadMask : true,
11451     
11452     /**
11453      * @cfg {Boolean} errorMask (true|false) default false
11454      */
11455     errorMask : false,
11456     
11457     /**
11458      * @cfg {Number} maskOffset Default 100
11459      */
11460     maskOffset : 100,
11461     
11462     /**
11463      * @cfg {Boolean} maskBody
11464      */
11465     maskBody : false,
11466
11467     getAutoCreate : function(){
11468
11469         var cfg = {
11470             tag: 'form',
11471             method : this.method || 'POST',
11472             id : this.id || Roo.id(),
11473             cls : ''
11474         };
11475         if (this.parent().xtype.match(/^Nav/)) {
11476             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11477
11478         }
11479
11480         if (this.labelAlign == 'left' ) {
11481             cfg.cls += ' form-horizontal';
11482         }
11483
11484
11485         return cfg;
11486     },
11487     initEvents : function()
11488     {
11489         this.el.on('submit', this.onSubmit, this);
11490         // this was added as random key presses on the form where triggering form submit.
11491         this.el.on('keypress', function(e) {
11492             if (e.getCharCode() != 13) {
11493                 return true;
11494             }
11495             // we might need to allow it for textareas.. and some other items.
11496             // check e.getTarget().
11497
11498             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11499                 return true;
11500             }
11501
11502             Roo.log("keypress blocked");
11503
11504             e.preventDefault();
11505             return false;
11506         });
11507         
11508     },
11509     // private
11510     onSubmit : function(e){
11511         e.stopEvent();
11512     },
11513
11514      /**
11515      * Returns true if client-side validation on the form is successful.
11516      * @return Boolean
11517      */
11518     isValid : function(){
11519         var items = this.getItems();
11520         var valid = true;
11521         var target = false;
11522         
11523         items.each(function(f){
11524             
11525             if(f.validate()){
11526                 return;
11527             }
11528             
11529             Roo.log('invalid field: ' + f.name);
11530             
11531             valid = false;
11532
11533             if(!target && f.el.isVisible(true)){
11534                 target = f;
11535             }
11536            
11537         });
11538         
11539         if(this.errorMask && !valid){
11540             Roo.bootstrap.form.Form.popover.mask(this, target);
11541         }
11542         
11543         return valid;
11544     },
11545     
11546     /**
11547      * Returns true if any fields in this form have changed since their original load.
11548      * @return Boolean
11549      */
11550     isDirty : function(){
11551         var dirty = false;
11552         var items = this.getItems();
11553         items.each(function(f){
11554            if(f.isDirty()){
11555                dirty = true;
11556                return false;
11557            }
11558            return true;
11559         });
11560         return dirty;
11561     },
11562      /**
11563      * Performs a predefined action (submit or load) or custom actions you define on this form.
11564      * @param {String} actionName The name of the action type
11565      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11566      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11567      * accept other config options):
11568      * <pre>
11569 Property          Type             Description
11570 ----------------  ---------------  ----------------------------------------------------------------------------------
11571 url               String           The url for the action (defaults to the form's url)
11572 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11573 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11574 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11575                                    validate the form on the client (defaults to false)
11576      * </pre>
11577      * @return {BasicForm} this
11578      */
11579     doAction : function(action, options){
11580         if(typeof action == 'string'){
11581             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11582         }
11583         if(this.fireEvent('beforeaction', this, action) !== false){
11584             this.beforeAction(action);
11585             action.run.defer(100, action);
11586         }
11587         return this;
11588     },
11589
11590     // private
11591     beforeAction : function(action){
11592         var o = action.options;
11593         
11594         if(this.loadMask){
11595             
11596             if(this.maskBody){
11597                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11598             } else {
11599                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11600             }
11601         }
11602         // not really supported yet.. ??
11603
11604         //if(this.waitMsgTarget === true){
11605         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11606         //}else if(this.waitMsgTarget){
11607         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11608         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11609         //}else {
11610         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11611        // }
11612
11613     },
11614
11615     // private
11616     afterAction : function(action, success){
11617         this.activeAction = null;
11618         var o = action.options;
11619
11620         if(this.loadMask){
11621             
11622             if(this.maskBody){
11623                 Roo.get(document.body).unmask();
11624             } else {
11625                 this.el.unmask();
11626             }
11627         }
11628         
11629         //if(this.waitMsgTarget === true){
11630 //            this.el.unmask();
11631         //}else if(this.waitMsgTarget){
11632         //    this.waitMsgTarget.unmask();
11633         //}else{
11634         //    Roo.MessageBox.updateProgress(1);
11635         //    Roo.MessageBox.hide();
11636        // }
11637         //
11638         if(success){
11639             if(o.reset){
11640                 this.reset();
11641             }
11642             Roo.callback(o.success, o.scope, [this, action]);
11643             this.fireEvent('actioncomplete', this, action);
11644
11645         }else{
11646
11647             // failure condition..
11648             // we have a scenario where updates need confirming.
11649             // eg. if a locking scenario exists..
11650             // we look for { errors : { needs_confirm : true }} in the response.
11651             if (
11652                 (typeof(action.result) != 'undefined')  &&
11653                 (typeof(action.result.errors) != 'undefined')  &&
11654                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11655            ){
11656                 var _t = this;
11657                 Roo.log("not supported yet");
11658                  /*
11659
11660                 Roo.MessageBox.confirm(
11661                     "Change requires confirmation",
11662                     action.result.errorMsg,
11663                     function(r) {
11664                         if (r != 'yes') {
11665                             return;
11666                         }
11667                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11668                     }
11669
11670                 );
11671                 */
11672
11673
11674                 return;
11675             }
11676
11677             Roo.callback(o.failure, o.scope, [this, action]);
11678             // show an error message if no failed handler is set..
11679             if (!this.hasListener('actionfailed')) {
11680                 Roo.log("need to add dialog support");
11681                 /*
11682                 Roo.MessageBox.alert("Error",
11683                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11684                         action.result.errorMsg :
11685                         "Saving Failed, please check your entries or try again"
11686                 );
11687                 */
11688             }
11689
11690             this.fireEvent('actionfailed', this, action);
11691         }
11692
11693     },
11694     /**
11695      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11696      * @param {String} id The value to search for
11697      * @return Field
11698      */
11699     findField : function(id){
11700         var items = this.getItems();
11701         var field = items.get(id);
11702         if(!field){
11703              items.each(function(f){
11704                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11705                     field = f;
11706                     return false;
11707                 }
11708                 return true;
11709             });
11710         }
11711         return field || null;
11712     },
11713      /**
11714      * Mark fields in this form invalid in bulk.
11715      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11716      * @return {BasicForm} this
11717      */
11718     markInvalid : function(errors){
11719         if(errors instanceof Array){
11720             for(var i = 0, len = errors.length; i < len; i++){
11721                 var fieldError = errors[i];
11722                 var f = this.findField(fieldError.id);
11723                 if(f){
11724                     f.markInvalid(fieldError.msg);
11725                 }
11726             }
11727         }else{
11728             var field, id;
11729             for(id in errors){
11730                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11731                     field.markInvalid(errors[id]);
11732                 }
11733             }
11734         }
11735         //Roo.each(this.childForms || [], function (f) {
11736         //    f.markInvalid(errors);
11737         //});
11738
11739         return this;
11740     },
11741
11742     /**
11743      * Set values for fields in this form in bulk.
11744      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11745      * @return {BasicForm} this
11746      */
11747     setValues : function(values){
11748         if(values instanceof Array){ // array of objects
11749             for(var i = 0, len = values.length; i < len; i++){
11750                 var v = values[i];
11751                 var f = this.findField(v.id);
11752                 if(f){
11753                     f.setValue(v.value);
11754                     if(this.trackResetOnLoad){
11755                         f.originalValue = f.getValue();
11756                     }
11757                 }
11758             }
11759         }else{ // object hash
11760             var field, id;
11761             for(id in values){
11762                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11763
11764                     if (field.setFromData &&
11765                         field.valueField &&
11766                         field.displayField &&
11767                         // combos' with local stores can
11768                         // be queried via setValue()
11769                         // to set their value..
11770                         (field.store && !field.store.isLocal)
11771                         ) {
11772                         // it's a combo
11773                         var sd = { };
11774                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11775                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11776                         field.setFromData(sd);
11777
11778                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11779                         
11780                         field.setFromData(values);
11781                         
11782                     } else {
11783                         field.setValue(values[id]);
11784                     }
11785
11786
11787                     if(this.trackResetOnLoad){
11788                         field.originalValue = field.getValue();
11789                     }
11790                 }
11791             }
11792         }
11793
11794         //Roo.each(this.childForms || [], function (f) {
11795         //    f.setValues(values);
11796         //});
11797
11798         return this;
11799     },
11800
11801     /**
11802      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11803      * they are returned as an array.
11804      * @param {Boolean} asString
11805      * @return {Object}
11806      */
11807     getValues : function(asString){
11808         //if (this.childForms) {
11809             // copy values from the child forms
11810         //    Roo.each(this.childForms, function (f) {
11811         //        this.setValues(f.getValues());
11812         //    }, this);
11813         //}
11814
11815
11816
11817         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11818         if(asString === true){
11819             return fs;
11820         }
11821         return Roo.urlDecode(fs);
11822     },
11823
11824     /**
11825      * Returns the fields in this form as an object with key/value pairs.
11826      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11827      * @return {Object}
11828      */
11829     getFieldValues : function(with_hidden)
11830     {
11831         var items = this.getItems();
11832         var ret = {};
11833         items.each(function(f){
11834             
11835             if (!f.getName()) {
11836                 return;
11837             }
11838             
11839             var v = f.getValue();
11840             
11841             if (f.inputType =='radio') {
11842                 if (typeof(ret[f.getName()]) == 'undefined') {
11843                     ret[f.getName()] = ''; // empty..
11844                 }
11845
11846                 if (!f.el.dom.checked) {
11847                     return;
11848
11849                 }
11850                 v = f.el.dom.value;
11851
11852             }
11853             
11854             if(f.xtype == 'MoneyField'){
11855                 ret[f.currencyName] = f.getCurrency();
11856             }
11857
11858             // not sure if this supported any more..
11859             if ((typeof(v) == 'object') && f.getRawValue) {
11860                 v = f.getRawValue() ; // dates..
11861             }
11862             // combo boxes where name != hiddenName...
11863             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11864                 ret[f.name] = f.getRawValue();
11865             }
11866             ret[f.getName()] = v;
11867         });
11868
11869         return ret;
11870     },
11871
11872     /**
11873      * Clears all invalid messages in this form.
11874      * @return {BasicForm} this
11875      */
11876     clearInvalid : function(){
11877         var items = this.getItems();
11878
11879         items.each(function(f){
11880            f.clearInvalid();
11881         });
11882
11883         return this;
11884     },
11885
11886     /**
11887      * Resets this form.
11888      * @return {BasicForm} this
11889      */
11890     reset : function(){
11891         var items = this.getItems();
11892         items.each(function(f){
11893             f.reset();
11894         });
11895
11896         Roo.each(this.childForms || [], function (f) {
11897             f.reset();
11898         });
11899
11900
11901         return this;
11902     },
11903     
11904     getItems : function()
11905     {
11906         var r=new Roo.util.MixedCollection(false, function(o){
11907             return o.id || (o.id = Roo.id());
11908         });
11909         var iter = function(el) {
11910             if (el.inputEl) {
11911                 r.add(el);
11912             }
11913             if (!el.items) {
11914                 return;
11915             }
11916             Roo.each(el.items,function(e) {
11917                 iter(e);
11918             });
11919         };
11920
11921         iter(this);
11922         return r;
11923     },
11924     
11925     hideFields : function(items)
11926     {
11927         Roo.each(items, function(i){
11928             
11929             var f = this.findField(i);
11930             
11931             if(!f){
11932                 return;
11933             }
11934             
11935             f.hide();
11936             
11937         }, this);
11938     },
11939     
11940     showFields : function(items)
11941     {
11942         Roo.each(items, function(i){
11943             
11944             var f = this.findField(i);
11945             
11946             if(!f){
11947                 return;
11948             }
11949             
11950             f.show();
11951             
11952         }, this);
11953     }
11954
11955 });
11956
11957 Roo.apply(Roo.bootstrap.form.Form, {
11958     
11959     popover : {
11960         
11961         padding : 5,
11962         
11963         isApplied : false,
11964         
11965         isMasked : false,
11966         
11967         form : false,
11968         
11969         target : false,
11970         
11971         toolTip : false,
11972         
11973         intervalID : false,
11974         
11975         maskEl : false,
11976         
11977         apply : function()
11978         {
11979             if(this.isApplied){
11980                 return;
11981             }
11982             
11983             this.maskEl = {
11984                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11985                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11986                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11987                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11988             };
11989             
11990             this.maskEl.top.enableDisplayMode("block");
11991             this.maskEl.left.enableDisplayMode("block");
11992             this.maskEl.bottom.enableDisplayMode("block");
11993             this.maskEl.right.enableDisplayMode("block");
11994             
11995             this.toolTip = new Roo.bootstrap.Tooltip({
11996                 cls : 'roo-form-error-popover',
11997                 alignment : {
11998                     'left' : ['r-l', [-2,0], 'right'],
11999                     'right' : ['l-r', [2,0], 'left'],
12000                     'bottom' : ['tl-bl', [0,2], 'top'],
12001                     'top' : [ 'bl-tl', [0,-2], 'bottom']
12002                 }
12003             });
12004             
12005             this.toolTip.render(Roo.get(document.body));
12006
12007             this.toolTip.el.enableDisplayMode("block");
12008             
12009             Roo.get(document.body).on('click', function(){
12010                 this.unmask();
12011             }, this);
12012             
12013             Roo.get(document.body).on('touchstart', function(){
12014                 this.unmask();
12015             }, this);
12016             
12017             this.isApplied = true
12018         },
12019         
12020         mask : function(form, target)
12021         {
12022             this.form = form;
12023             
12024             this.target = target;
12025             
12026             if(!this.form.errorMask || !target.el){
12027                 return;
12028             }
12029             
12030             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12031             
12032             Roo.log(scrollable);
12033             
12034             var ot = this.target.el.calcOffsetsTo(scrollable);
12035             
12036             var scrollTo = ot[1] - this.form.maskOffset;
12037             
12038             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12039             
12040             scrollable.scrollTo('top', scrollTo);
12041             
12042             var box = this.target.el.getBox();
12043             Roo.log(box);
12044             var zIndex = Roo.bootstrap.Modal.zIndex++;
12045
12046             
12047             this.maskEl.top.setStyle('position', 'absolute');
12048             this.maskEl.top.setStyle('z-index', zIndex);
12049             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12050             this.maskEl.top.setLeft(0);
12051             this.maskEl.top.setTop(0);
12052             this.maskEl.top.show();
12053             
12054             this.maskEl.left.setStyle('position', 'absolute');
12055             this.maskEl.left.setStyle('z-index', zIndex);
12056             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12057             this.maskEl.left.setLeft(0);
12058             this.maskEl.left.setTop(box.y - this.padding);
12059             this.maskEl.left.show();
12060
12061             this.maskEl.bottom.setStyle('position', 'absolute');
12062             this.maskEl.bottom.setStyle('z-index', zIndex);
12063             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12064             this.maskEl.bottom.setLeft(0);
12065             this.maskEl.bottom.setTop(box.bottom + this.padding);
12066             this.maskEl.bottom.show();
12067
12068             this.maskEl.right.setStyle('position', 'absolute');
12069             this.maskEl.right.setStyle('z-index', zIndex);
12070             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12071             this.maskEl.right.setLeft(box.right + this.padding);
12072             this.maskEl.right.setTop(box.y - this.padding);
12073             this.maskEl.right.show();
12074
12075             this.toolTip.bindEl = this.target.el;
12076
12077             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12078
12079             var tip = this.target.blankText;
12080
12081             if(this.target.getValue() !== '' ) {
12082                 
12083                 if (this.target.invalidText.length) {
12084                     tip = this.target.invalidText;
12085                 } else if (this.target.regexText.length){
12086                     tip = this.target.regexText;
12087                 }
12088             }
12089
12090             this.toolTip.show(tip);
12091
12092             this.intervalID = window.setInterval(function() {
12093                 Roo.bootstrap.form.Form.popover.unmask();
12094             }, 10000);
12095
12096             window.onwheel = function(){ return false;};
12097             
12098             (function(){ this.isMasked = true; }).defer(500, this);
12099             
12100         },
12101         
12102         unmask : function()
12103         {
12104             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12105                 return;
12106             }
12107             
12108             this.maskEl.top.setStyle('position', 'absolute');
12109             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12110             this.maskEl.top.hide();
12111
12112             this.maskEl.left.setStyle('position', 'absolute');
12113             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12114             this.maskEl.left.hide();
12115
12116             this.maskEl.bottom.setStyle('position', 'absolute');
12117             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12118             this.maskEl.bottom.hide();
12119
12120             this.maskEl.right.setStyle('position', 'absolute');
12121             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12122             this.maskEl.right.hide();
12123             
12124             this.toolTip.hide();
12125             
12126             this.toolTip.el.hide();
12127             
12128             window.onwheel = function(){ return true;};
12129             
12130             if(this.intervalID){
12131                 window.clearInterval(this.intervalID);
12132                 this.intervalID = false;
12133             }
12134             
12135             this.isMasked = false;
12136             
12137         }
12138         
12139     }
12140     
12141 });
12142
12143 /*
12144  * Based on:
12145  * Ext JS Library 1.1.1
12146  * Copyright(c) 2006-2007, Ext JS, LLC.
12147  *
12148  * Originally Released Under LGPL - original licence link has changed is not relivant.
12149  *
12150  * Fork - LGPL
12151  * <script type="text/javascript">
12152  */
12153 /**
12154  * @class Roo.form.VTypes
12155  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12156  * @static
12157  */
12158 Roo.form.VTypes = function(){
12159     // closure these in so they are only created once.
12160     var alpha = /^[a-zA-Z_]+$/;
12161     var alphanum = /^[a-zA-Z0-9_]+$/;
12162     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12163     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12164
12165     // All these messages and functions are configurable
12166     return {
12167         /**
12168          * The function used to validate email addresses
12169          * @param {String} value The email address
12170          */
12171         'email' : function(v){
12172             return email.test(v);
12173         },
12174         /**
12175          * The error text to display when the email validation function returns false
12176          * @type String
12177          */
12178         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12179         /**
12180          * The keystroke filter mask to be applied on email input
12181          * @type RegExp
12182          */
12183         'emailMask' : /[a-z0-9_\.\-@]/i,
12184
12185         /**
12186          * The function used to validate URLs
12187          * @param {String} value The URL
12188          */
12189         'url' : function(v){
12190             return url.test(v);
12191         },
12192         /**
12193          * The error text to display when the url validation function returns false
12194          * @type String
12195          */
12196         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12197         
12198         /**
12199          * The function used to validate alpha values
12200          * @param {String} value The value
12201          */
12202         'alpha' : function(v){
12203             return alpha.test(v);
12204         },
12205         /**
12206          * The error text to display when the alpha validation function returns false
12207          * @type String
12208          */
12209         'alphaText' : 'This field should only contain letters and _',
12210         /**
12211          * The keystroke filter mask to be applied on alpha input
12212          * @type RegExp
12213          */
12214         'alphaMask' : /[a-z_]/i,
12215
12216         /**
12217          * The function used to validate alphanumeric values
12218          * @param {String} value The value
12219          */
12220         'alphanum' : function(v){
12221             return alphanum.test(v);
12222         },
12223         /**
12224          * The error text to display when the alphanumeric validation function returns false
12225          * @type String
12226          */
12227         'alphanumText' : 'This field should only contain letters, numbers and _',
12228         /**
12229          * The keystroke filter mask to be applied on alphanumeric input
12230          * @type RegExp
12231          */
12232         'alphanumMask' : /[a-z0-9_]/i
12233     };
12234 }();/*
12235  * - LGPL
12236  *
12237  * Input
12238  * 
12239  */
12240
12241 /**
12242  * @class Roo.bootstrap.form.Input
12243  * @extends Roo.bootstrap.Component
12244  * Bootstrap Input class
12245  * @cfg {Boolean} disabled is it disabled
12246  * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)  
12247  * @cfg {String} name name of the input
12248  * @cfg {string} fieldLabel - the label associated
12249  * @cfg {string} placeholder - placeholder to put in text.
12250  * @cfg {string} before - input group add on before
12251  * @cfg {string} after - input group add on after
12252  * @cfg {string} size - (lg|sm) or leave empty..
12253  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12254  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12255  * @cfg {Number} md colspan out of 12 for computer-sized screens
12256  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12257  * @cfg {string} value default value of the input
12258  * @cfg {Number} labelWidth set the width of label 
12259  * @cfg {Number} labellg set the width of label (1-12)
12260  * @cfg {Number} labelmd set the width of label (1-12)
12261  * @cfg {Number} labelsm set the width of label (1-12)
12262  * @cfg {Number} labelxs set the width of label (1-12)
12263  * @cfg {String} labelAlign (top|left)
12264  * @cfg {Boolean} readOnly Specifies that the field should be read-only
12265  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12266  * @cfg {String} indicatorpos (left|right) default left
12267  * @cfg {String} capture (user|camera) use for file input only. (default empty)
12268  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12269  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12270  * @cfg {Roo.bootstrap.Button} before Button to show before
12271  * @cfg {Roo.bootstrap.Button} afterButton to show before
12272  * @cfg {String} align (left|center|right) Default left
12273  * @cfg {Boolean} forceFeedback (true|false) Default false
12274  * 
12275  * @constructor
12276  * Create a new Input
12277  * @param {Object} config The config object
12278  */
12279
12280 Roo.bootstrap.form.Input = function(config){
12281     
12282     Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12283     
12284     this.addEvents({
12285         /**
12286          * @event focus
12287          * Fires when this field receives input focus.
12288          * @param {Roo.form.Field} this
12289          */
12290         focus : true,
12291         /**
12292          * @event blur
12293          * Fires when this field loses input focus.
12294          * @param {Roo.form.Field} this
12295          */
12296         blur : true,
12297         /**
12298          * @event specialkey
12299          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
12300          * {@link Roo.EventObject#getKey} to determine which key was pressed.
12301          * @param {Roo.form.Field} this
12302          * @param {Roo.EventObject} e The event object
12303          */
12304         specialkey : true,
12305         /**
12306          * @event change
12307          * Fires just before the field blurs if the field value has changed.
12308          * @param {Roo.form.Field} this
12309          * @param {Mixed} newValue The new value
12310          * @param {Mixed} oldValue The original value
12311          */
12312         change : true,
12313         /**
12314          * @event invalid
12315          * Fires after the field has been marked as invalid.
12316          * @param {Roo.form.Field} this
12317          * @param {String} msg The validation message
12318          */
12319         invalid : true,
12320         /**
12321          * @event valid
12322          * Fires after the field has been validated with no errors.
12323          * @param {Roo.form.Field} this
12324          */
12325         valid : true,
12326          /**
12327          * @event keyup
12328          * Fires after the key up
12329          * @param {Roo.form.Field} this
12330          * @param {Roo.EventObject}  e The event Object
12331          */
12332         keyup : true,
12333         /**
12334          * @event paste
12335          * Fires after the user pastes into input
12336          * @param {Roo.form.Field} this
12337          * @param {Roo.EventObject}  e The event Object
12338          */
12339         paste : true
12340     });
12341 };
12342
12343 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component,  {
12344      /**
12345      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12346       automatic validation (defaults to "keyup").
12347      */
12348     validationEvent : "keyup",
12349      /**
12350      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12351      */
12352     validateOnBlur : true,
12353     /**
12354      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12355      */
12356     validationDelay : 250,
12357      /**
12358      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12359      */
12360     focusClass : "x-form-focus",  // not needed???
12361     
12362        
12363     /**
12364      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12365      */
12366     invalidClass : "has-warning",
12367     
12368     /**
12369      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12370      */
12371     validClass : "has-success",
12372     
12373     /**
12374      * @cfg {Boolean} hasFeedback (true|false) default true
12375      */
12376     hasFeedback : true,
12377     
12378     /**
12379      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12380      */
12381     invalidFeedbackClass : "glyphicon-warning-sign",
12382     
12383     /**
12384      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12385      */
12386     validFeedbackClass : "glyphicon-ok",
12387     
12388     /**
12389      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12390      */
12391     selectOnFocus : false,
12392     
12393      /**
12394      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12395      */
12396     maskRe : null,
12397        /**
12398      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12399      */
12400     vtype : null,
12401     
12402       /**
12403      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12404      */
12405     disableKeyFilter : false,
12406     
12407        /**
12408      * @cfg {Boolean} disabled True to disable the field (defaults to false).
12409      */
12410     disabled : false,
12411      /**
12412      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12413      */
12414     allowBlank : true,
12415     /**
12416      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12417      */
12418     blankText : "Please complete this mandatory field",
12419     
12420      /**
12421      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12422      */
12423     minLength : 0,
12424     /**
12425      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12426      */
12427     maxLength : Number.MAX_VALUE,
12428     /**
12429      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12430      */
12431     minLengthText : "The minimum length for this field is {0}",
12432     /**
12433      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12434      */
12435     maxLengthText : "The maximum length for this field is {0}",
12436   
12437     
12438     /**
12439      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12440      * If available, this function will be called only after the basic validators all return true, and will be passed the
12441      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12442      */
12443     validator : null,
12444     /**
12445      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12446      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12447      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
12448      */
12449     regex : null,
12450     /**
12451      * @cfg {String} regexText -- Depricated - use Invalid Text
12452      */
12453     regexText : "",
12454     
12455     /**
12456      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12457      */
12458     invalidText : "",
12459     
12460     
12461     
12462     autocomplete: false,
12463     
12464     
12465     fieldLabel : '',
12466     inputType : 'text',
12467     
12468     name : false,
12469     placeholder: false,
12470     before : false,
12471     after : false,
12472     size : false,
12473     hasFocus : false,
12474     preventMark: false,
12475     isFormField : true,
12476     value : '',
12477     labelWidth : 2,
12478     labelAlign : false,
12479     readOnly : false,
12480     align : false,
12481     formatedValue : false,
12482     forceFeedback : false,
12483     
12484     indicatorpos : 'left',
12485     
12486     labellg : 0,
12487     labelmd : 0,
12488     labelsm : 0,
12489     labelxs : 0,
12490     
12491     capture : '',
12492     accept : '',
12493     
12494     parentLabelAlign : function()
12495     {
12496         var parent = this;
12497         while (parent.parent()) {
12498             parent = parent.parent();
12499             if (typeof(parent.labelAlign) !='undefined') {
12500                 return parent.labelAlign;
12501             }
12502         }
12503         return 'left';
12504         
12505     },
12506     
12507     getAutoCreate : function()
12508     {
12509         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12510         
12511         var id = Roo.id();
12512         
12513         var cfg = {};
12514         
12515         if(this.inputType != 'hidden'){
12516             cfg.cls = 'form-group' //input-group
12517         }
12518         
12519         var input =  {
12520             tag: 'input',
12521             id : id,
12522             type : this.inputType,
12523             value : this.value,
12524             cls : 'form-control',
12525             placeholder : this.placeholder || '',
12526             autocomplete : this.autocomplete || 'new-password'
12527         };
12528         if (this.inputType == 'file') {
12529             input.style = 'overflow:hidden'; // why not in CSS?
12530         }
12531         
12532         if(this.capture.length){
12533             input.capture = this.capture;
12534         }
12535         
12536         if(this.accept.length){
12537             input.accept = this.accept + "/*";
12538         }
12539         
12540         if(this.align){
12541             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12542         }
12543         
12544         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12545             input.maxLength = this.maxLength;
12546         }
12547         
12548         if (this.disabled) {
12549             input.disabled=true;
12550         }
12551         
12552         if (this.readOnly) {
12553             input.readonly=true;
12554         }
12555         
12556         if (this.name) {
12557             input.name = this.name;
12558         }
12559         
12560         if (this.size) {
12561             input.cls += ' input-' + this.size;
12562         }
12563         
12564         var settings=this;
12565         ['xs','sm','md','lg'].map(function(size){
12566             if (settings[size]) {
12567                 cfg.cls += ' col-' + size + '-' + settings[size];
12568             }
12569         });
12570         
12571         var inputblock = input;
12572         
12573         var feedback = {
12574             tag: 'span',
12575             cls: 'glyphicon form-control-feedback'
12576         };
12577             
12578         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12579             
12580             inputblock = {
12581                 cls : 'has-feedback',
12582                 cn :  [
12583                     input,
12584                     feedback
12585                 ] 
12586             };  
12587         }
12588         
12589         if (this.before || this.after) {
12590             
12591             inputblock = {
12592                 cls : 'input-group',
12593                 cn :  [] 
12594             };
12595             
12596             if (this.before && typeof(this.before) == 'string') {
12597                 
12598                 inputblock.cn.push({
12599                     tag :'span',
12600                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12601                     html : this.before
12602                 });
12603             }
12604             if (this.before && typeof(this.before) == 'object') {
12605                 this.before = Roo.factory(this.before);
12606                 
12607                 inputblock.cn.push({
12608                     tag :'span',
12609                     cls : 'roo-input-before input-group-prepend   input-group-' +
12610                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12611                 });
12612             }
12613             
12614             inputblock.cn.push(input);
12615             
12616             if (this.after && typeof(this.after) == 'string') {
12617                 inputblock.cn.push({
12618                     tag :'span',
12619                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12620                     html : this.after
12621                 });
12622             }
12623             if (this.after && typeof(this.after) == 'object') {
12624                 this.after = Roo.factory(this.after);
12625                 
12626                 inputblock.cn.push({
12627                     tag :'span',
12628                     cls : 'roo-input-after input-group-append  input-group-' +
12629                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12630                 });
12631             }
12632             
12633             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12634                 inputblock.cls += ' has-feedback';
12635                 inputblock.cn.push(feedback);
12636             }
12637         };
12638         var indicator = {
12639             tag : 'i',
12640             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12641             tooltip : 'This field is required'
12642         };
12643         if (this.allowBlank ) {
12644             indicator.style = this.allowBlank ? ' display:none' : '';
12645         }
12646         if (align ==='left' && this.fieldLabel.length) {
12647             
12648             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12649             
12650             cfg.cn = [
12651                 indicator,
12652                 {
12653                     tag: 'label',
12654                     'for' :  id,
12655                     cls : 'control-label col-form-label',
12656                     html : this.fieldLabel
12657
12658                 },
12659                 {
12660                     cls : "", 
12661                     cn: [
12662                         inputblock
12663                     ]
12664                 }
12665             ];
12666             
12667             var labelCfg = cfg.cn[1];
12668             var contentCfg = cfg.cn[2];
12669             
12670             if(this.indicatorpos == 'right'){
12671                 cfg.cn = [
12672                     {
12673                         tag: 'label',
12674                         'for' :  id,
12675                         cls : 'control-label col-form-label',
12676                         cn : [
12677                             {
12678                                 tag : 'span',
12679                                 html : this.fieldLabel
12680                             },
12681                             indicator
12682                         ]
12683                     },
12684                     {
12685                         cls : "",
12686                         cn: [
12687                             inputblock
12688                         ]
12689                     }
12690
12691                 ];
12692                 
12693                 labelCfg = cfg.cn[0];
12694                 contentCfg = cfg.cn[1];
12695             
12696             }
12697             
12698             if(this.labelWidth > 12){
12699                 labelCfg.style = "width: " + this.labelWidth + 'px';
12700             }
12701             
12702             if(this.labelWidth < 13 && this.labelmd == 0){
12703                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12704             }
12705             
12706             if(this.labellg > 0){
12707                 labelCfg.cls += ' col-lg-' + this.labellg;
12708                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12709             }
12710             
12711             if(this.labelmd > 0){
12712                 labelCfg.cls += ' col-md-' + this.labelmd;
12713                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12714             }
12715             
12716             if(this.labelsm > 0){
12717                 labelCfg.cls += ' col-sm-' + this.labelsm;
12718                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12719             }
12720             
12721             if(this.labelxs > 0){
12722                 labelCfg.cls += ' col-xs-' + this.labelxs;
12723                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12724             }
12725             
12726             
12727         } else if ( this.fieldLabel.length) {
12728                 
12729             
12730             
12731             cfg.cn = [
12732                 {
12733                     tag : 'i',
12734                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12735                     tooltip : 'This field is required',
12736                     style : this.allowBlank ? ' display:none' : '' 
12737                 },
12738                 {
12739                     tag: 'label',
12740                    //cls : 'input-group-addon',
12741                     html : this.fieldLabel
12742
12743                 },
12744
12745                inputblock
12746
12747            ];
12748            
12749            if(this.indicatorpos == 'right'){
12750        
12751                 cfg.cn = [
12752                     {
12753                         tag: 'label',
12754                        //cls : 'input-group-addon',
12755                         html : this.fieldLabel
12756
12757                     },
12758                     {
12759                         tag : 'i',
12760                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12761                         tooltip : 'This field is required',
12762                         style : this.allowBlank ? ' display:none' : '' 
12763                     },
12764
12765                    inputblock
12766
12767                ];
12768
12769             }
12770
12771         } else {
12772             
12773             cfg.cn = [
12774
12775                     inputblock
12776
12777             ];
12778                 
12779                 
12780         };
12781         
12782         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12783            cfg.cls += ' navbar-form';
12784         }
12785         
12786         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12787             // on BS4 we do this only if not form 
12788             cfg.cls += ' navbar-form';
12789             cfg.tag = 'li';
12790         }
12791         
12792         return cfg;
12793         
12794     },
12795     /**
12796      * return the real input element.
12797      */
12798     inputEl: function ()
12799     {
12800         return this.el.select('input.form-control',true).first();
12801     },
12802     
12803     tooltipEl : function()
12804     {
12805         return this.inputEl();
12806     },
12807     
12808     indicatorEl : function()
12809     {
12810         if (Roo.bootstrap.version == 4) {
12811             return false; // not enabled in v4 yet.
12812         }
12813         
12814         var indicator = this.el.select('i.roo-required-indicator',true).first();
12815         
12816         if(!indicator){
12817             return false;
12818         }
12819         
12820         return indicator;
12821         
12822     },
12823     
12824     setDisabled : function(v)
12825     {
12826         var i  = this.inputEl().dom;
12827         if (!v) {
12828             i.removeAttribute('disabled');
12829             return;
12830             
12831         }
12832         i.setAttribute('disabled','true');
12833     },
12834     initEvents : function()
12835     {
12836           
12837         this.inputEl().on("keydown" , this.fireKey,  this);
12838         this.inputEl().on("focus", this.onFocus,  this);
12839         this.inputEl().on("blur", this.onBlur,  this);
12840         
12841         this.inputEl().relayEvent('keyup', this);
12842         this.inputEl().relayEvent('paste', this);
12843         
12844         this.indicator = this.indicatorEl();
12845         
12846         if(this.indicator){
12847             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12848         }
12849  
12850         // reference to original value for reset
12851         this.originalValue = this.getValue();
12852         //Roo.form.TextField.superclass.initEvents.call(this);
12853         if(this.validationEvent == 'keyup'){
12854             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12855             this.inputEl().on('keyup', this.filterValidation, this);
12856         }
12857         else if(this.validationEvent !== false){
12858             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12859         }
12860         
12861         if(this.selectOnFocus){
12862             this.on("focus", this.preFocus, this);
12863             
12864         }
12865         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12866             this.inputEl().on("keypress", this.filterKeys, this);
12867         } else {
12868             this.inputEl().relayEvent('keypress', this);
12869         }
12870        /* if(this.grow){
12871             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12872             this.el.on("click", this.autoSize,  this);
12873         }
12874         */
12875         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12876             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12877         }
12878         
12879         if (typeof(this.before) == 'object') {
12880             this.before.render(this.el.select('.roo-input-before',true).first());
12881         }
12882         if (typeof(this.after) == 'object') {
12883             this.after.render(this.el.select('.roo-input-after',true).first());
12884         }
12885         
12886         this.inputEl().on('change', this.onChange, this);
12887         
12888     },
12889     filterValidation : function(e){
12890         if(!e.isNavKeyPress()){
12891             this.validationTask.delay(this.validationDelay);
12892         }
12893     },
12894      /**
12895      * Validates the field value
12896      * @return {Boolean} True if the value is valid, else false
12897      */
12898     validate : function(){
12899         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12900         if(this.disabled || this.validateValue(this.getRawValue())){
12901             this.markValid();
12902             return true;
12903         }
12904         
12905         this.markInvalid();
12906         return false;
12907     },
12908     
12909     
12910     /**
12911      * Validates a value according to the field's validation rules and marks the field as invalid
12912      * if the validation fails
12913      * @param {Mixed} value The value to validate
12914      * @return {Boolean} True if the value is valid, else false
12915      */
12916     validateValue : function(value)
12917     {
12918         if(this.getVisibilityEl().hasClass('hidden')){
12919             return true;
12920         }
12921         
12922         if(value.length < 1)  { // if it's blank
12923             if(this.allowBlank){
12924                 return true;
12925             }
12926             return false;
12927         }
12928         
12929         if(value.length < this.minLength){
12930             return false;
12931         }
12932         if(value.length > this.maxLength){
12933             return false;
12934         }
12935         if(this.vtype){
12936             var vt = Roo.form.VTypes;
12937             if(!vt[this.vtype](value, this)){
12938                 return false;
12939             }
12940         }
12941         if(typeof this.validator == "function"){
12942             var msg = this.validator(value);
12943             if(msg !== true){
12944                 return false;
12945             }
12946             if (typeof(msg) == 'string') {
12947                 this.invalidText = msg;
12948             }
12949         }
12950         
12951         if(this.regex && !this.regex.test(value)){
12952             return false;
12953         }
12954         
12955         return true;
12956     },
12957     
12958      // private
12959     fireKey : function(e){
12960         //Roo.log('field ' + e.getKey());
12961         if(e.isNavKeyPress()){
12962             this.fireEvent("specialkey", this, e);
12963         }
12964     },
12965     focus : function (selectText){
12966         if(this.rendered){
12967             this.inputEl().focus();
12968             if(selectText === true){
12969                 this.inputEl().dom.select();
12970             }
12971         }
12972         return this;
12973     } ,
12974     
12975     onFocus : function(){
12976         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12977            // this.el.addClass(this.focusClass);
12978         }
12979         if(!this.hasFocus){
12980             this.hasFocus = true;
12981             this.startValue = this.getValue();
12982             this.fireEvent("focus", this);
12983         }
12984     },
12985     
12986     beforeBlur : Roo.emptyFn,
12987
12988     
12989     // private
12990     onBlur : function(){
12991         this.beforeBlur();
12992         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12993             //this.el.removeClass(this.focusClass);
12994         }
12995         this.hasFocus = false;
12996         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12997             this.validate();
12998         }
12999         var v = this.getValue();
13000         if(String(v) !== String(this.startValue)){
13001             this.fireEvent('change', this, v, this.startValue);
13002         }
13003         this.fireEvent("blur", this);
13004     },
13005     
13006     onChange : function(e)
13007     {
13008         var v = this.getValue();
13009         if(String(v) !== String(this.startValue)){
13010             this.fireEvent('change', this, v, this.startValue);
13011         }
13012         
13013     },
13014     
13015     /**
13016      * Resets the current field value to the originally loaded value and clears any validation messages
13017      */
13018     reset : function(){
13019         this.setValue(this.originalValue);
13020         this.validate();
13021     },
13022      /**
13023      * Returns the name of the field
13024      * @return {Mixed} name The name field
13025      */
13026     getName: function(){
13027         return this.name;
13028     },
13029      /**
13030      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
13031      * @return {Mixed} value The field value
13032      */
13033     getValue : function(){
13034         
13035         var v = this.inputEl().getValue();
13036         
13037         return v;
13038     },
13039     /**
13040      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
13041      * @return {Mixed} value The field value
13042      */
13043     getRawValue : function(){
13044         var v = this.inputEl().getValue();
13045         
13046         return v;
13047     },
13048     
13049     /**
13050      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
13051      * @param {Mixed} value The value to set
13052      */
13053     setRawValue : function(v){
13054         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13055     },
13056     
13057     selectText : function(start, end){
13058         var v = this.getRawValue();
13059         if(v.length > 0){
13060             start = start === undefined ? 0 : start;
13061             end = end === undefined ? v.length : end;
13062             var d = this.inputEl().dom;
13063             if(d.setSelectionRange){
13064                 d.setSelectionRange(start, end);
13065             }else if(d.createTextRange){
13066                 var range = d.createTextRange();
13067                 range.moveStart("character", start);
13068                 range.moveEnd("character", v.length-end);
13069                 range.select();
13070             }
13071         }
13072     },
13073     
13074     /**
13075      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
13076      * @param {Mixed} value The value to set
13077      */
13078     setValue : function(v){
13079         this.value = v;
13080         if(this.rendered){
13081             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13082             this.validate();
13083         }
13084     },
13085     
13086     /*
13087     processValue : function(value){
13088         if(this.stripCharsRe){
13089             var newValue = value.replace(this.stripCharsRe, '');
13090             if(newValue !== value){
13091                 this.setRawValue(newValue);
13092                 return newValue;
13093             }
13094         }
13095         return value;
13096     },
13097   */
13098     preFocus : function(){
13099         
13100         if(this.selectOnFocus){
13101             this.inputEl().dom.select();
13102         }
13103     },
13104     filterKeys : function(e){
13105         var k = e.getKey();
13106         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13107             return;
13108         }
13109         var c = e.getCharCode(), cc = String.fromCharCode(c);
13110         if(Roo.isIE && (e.isSpecialKey() || !cc)){
13111             return;
13112         }
13113         if(!this.maskRe.test(cc)){
13114             e.stopEvent();
13115         }
13116     },
13117      /**
13118      * Clear any invalid styles/messages for this field
13119      */
13120     clearInvalid : function(){
13121         
13122         if(!this.el || this.preventMark){ // not rendered
13123             return;
13124         }
13125         
13126         
13127         this.el.removeClass([this.invalidClass, 'is-invalid']);
13128         
13129         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13130             
13131             var feedback = this.el.select('.form-control-feedback', true).first();
13132             
13133             if(feedback){
13134                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13135             }
13136             
13137         }
13138         
13139         if(this.indicator){
13140             this.indicator.removeClass('visible');
13141             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13142         }
13143         
13144         this.fireEvent('valid', this);
13145     },
13146     
13147      /**
13148      * Mark this field as valid
13149      */
13150     markValid : function()
13151     {
13152         if(!this.el  || this.preventMark){ // not rendered...
13153             return;
13154         }
13155         
13156         this.el.removeClass([this.invalidClass, this.validClass]);
13157         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13158
13159         var feedback = this.el.select('.form-control-feedback', true).first();
13160             
13161         if(feedback){
13162             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13163         }
13164         
13165         if(this.indicator){
13166             this.indicator.removeClass('visible');
13167             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13168         }
13169         
13170         if(this.disabled){
13171             return;
13172         }
13173         
13174            
13175         if(this.allowBlank && !this.getRawValue().length){
13176             return;
13177         }
13178         if (Roo.bootstrap.version == 3) {
13179             this.el.addClass(this.validClass);
13180         } else {
13181             this.inputEl().addClass('is-valid');
13182         }
13183
13184         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13185             
13186             var feedback = this.el.select('.form-control-feedback', true).first();
13187             
13188             if(feedback){
13189                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13190                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13191             }
13192             
13193         }
13194         
13195         this.fireEvent('valid', this);
13196     },
13197     
13198      /**
13199      * Mark this field as invalid
13200      * @param {String} msg The validation message
13201      */
13202     markInvalid : function(msg)
13203     {
13204         if(!this.el  || this.preventMark){ // not rendered
13205             return;
13206         }
13207         
13208         this.el.removeClass([this.invalidClass, this.validClass]);
13209         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13210         
13211         var feedback = this.el.select('.form-control-feedback', true).first();
13212             
13213         if(feedback){
13214             this.el.select('.form-control-feedback', true).first().removeClass(
13215                     [this.invalidFeedbackClass, this.validFeedbackClass]);
13216         }
13217
13218         if(this.disabled){
13219             return;
13220         }
13221         
13222         if(this.allowBlank && !this.getRawValue().length){
13223             return;
13224         }
13225         
13226         if(this.indicator){
13227             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13228             this.indicator.addClass('visible');
13229         }
13230         if (Roo.bootstrap.version == 3) {
13231             this.el.addClass(this.invalidClass);
13232         } else {
13233             this.inputEl().addClass('is-invalid');
13234         }
13235         
13236         
13237         
13238         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13239             
13240             var feedback = this.el.select('.form-control-feedback', true).first();
13241             
13242             if(feedback){
13243                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244                 
13245                 if(this.getValue().length || this.forceFeedback){
13246                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13247                 }
13248                 
13249             }
13250             
13251         }
13252         
13253         this.fireEvent('invalid', this, msg);
13254     },
13255     // private
13256     SafariOnKeyDown : function(event)
13257     {
13258         // this is a workaround for a password hang bug on chrome/ webkit.
13259         if (this.inputEl().dom.type != 'password') {
13260             return;
13261         }
13262         
13263         var isSelectAll = false;
13264         
13265         if(this.inputEl().dom.selectionEnd > 0){
13266             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13267         }
13268         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13269             event.preventDefault();
13270             this.setValue('');
13271             return;
13272         }
13273         
13274         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13275             
13276             event.preventDefault();
13277             // this is very hacky as keydown always get's upper case.
13278             //
13279             var cc = String.fromCharCode(event.getCharCode());
13280             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
13281             
13282         }
13283     },
13284     adjustWidth : function(tag, w){
13285         tag = tag.toLowerCase();
13286         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13287             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13288                 if(tag == 'input'){
13289                     return w + 2;
13290                 }
13291                 if(tag == 'textarea'){
13292                     return w-2;
13293                 }
13294             }else if(Roo.isOpera){
13295                 if(tag == 'input'){
13296                     return w + 2;
13297                 }
13298                 if(tag == 'textarea'){
13299                     return w-2;
13300                 }
13301             }
13302         }
13303         return w;
13304     },
13305     
13306     setFieldLabel : function(v)
13307     {
13308         if(!this.rendered){
13309             return;
13310         }
13311         
13312         if(this.indicatorEl()){
13313             var ar = this.el.select('label > span',true);
13314             
13315             if (ar.elements.length) {
13316                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13317                 this.fieldLabel = v;
13318                 return;
13319             }
13320             
13321             var br = this.el.select('label',true);
13322             
13323             if(br.elements.length) {
13324                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13325                 this.fieldLabel = v;
13326                 return;
13327             }
13328             
13329             Roo.log('Cannot Found any of label > span || label in input');
13330             return;
13331         }
13332         
13333         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13334         this.fieldLabel = v;
13335         
13336         
13337     }
13338 });
13339
13340  
13341 /*
13342  * - LGPL
13343  *
13344  * Input
13345  * 
13346  */
13347
13348 /**
13349  * @class Roo.bootstrap.form.TextArea
13350  * @extends Roo.bootstrap.form.Input
13351  * Bootstrap TextArea class
13352  * @cfg {Number} cols Specifies the visible width of a text area
13353  * @cfg {Number} rows Specifies the visible number of lines in a text area
13354  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13355  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13356  * @cfg {string} html text
13357  * 
13358  * @constructor
13359  * Create a new TextArea
13360  * @param {Object} config The config object
13361  */
13362
13363 Roo.bootstrap.form.TextArea = function(config){
13364     Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13365    
13366 };
13367
13368 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input,  {
13369      
13370     cols : false,
13371     rows : 5,
13372     readOnly : false,
13373     warp : 'soft',
13374     resize : false,
13375     value: false,
13376     html: false,
13377     
13378     getAutoCreate : function(){
13379         
13380         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13381         
13382         var id = Roo.id();
13383         
13384         var cfg = {};
13385         
13386         if(this.inputType != 'hidden'){
13387             cfg.cls = 'form-group' //input-group
13388         }
13389         
13390         var input =  {
13391             tag: 'textarea',
13392             id : id,
13393             warp : this.warp,
13394             rows : this.rows,
13395             value : this.value || '',
13396             html: this.html || '',
13397             cls : 'form-control',
13398             placeholder : this.placeholder || '' 
13399             
13400         };
13401         
13402         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13403             input.maxLength = this.maxLength;
13404         }
13405         
13406         if(this.resize){
13407             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13408         }
13409         
13410         if(this.cols){
13411             input.cols = this.cols;
13412         }
13413         
13414         if (this.readOnly) {
13415             input.readonly = true;
13416         }
13417         
13418         if (this.name) {
13419             input.name = this.name;
13420         }
13421         
13422         if (this.size) {
13423             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13424         }
13425         
13426         var settings=this;
13427         ['xs','sm','md','lg'].map(function(size){
13428             if (settings[size]) {
13429                 cfg.cls += ' col-' + size + '-' + settings[size];
13430             }
13431         });
13432         
13433         var inputblock = input;
13434         
13435         if(this.hasFeedback && !this.allowBlank){
13436             
13437             var feedback = {
13438                 tag: 'span',
13439                 cls: 'glyphicon form-control-feedback'
13440             };
13441
13442             inputblock = {
13443                 cls : 'has-feedback',
13444                 cn :  [
13445                     input,
13446                     feedback
13447                 ] 
13448             };  
13449         }
13450         
13451         
13452         if (this.before || this.after) {
13453             
13454             inputblock = {
13455                 cls : 'input-group',
13456                 cn :  [] 
13457             };
13458             if (this.before) {
13459                 inputblock.cn.push({
13460                     tag :'span',
13461                     cls : 'input-group-addon',
13462                     html : this.before
13463                 });
13464             }
13465             
13466             inputblock.cn.push(input);
13467             
13468             if(this.hasFeedback && !this.allowBlank){
13469                 inputblock.cls += ' has-feedback';
13470                 inputblock.cn.push(feedback);
13471             }
13472             
13473             if (this.after) {
13474                 inputblock.cn.push({
13475                     tag :'span',
13476                     cls : 'input-group-addon',
13477                     html : this.after
13478                 });
13479             }
13480             
13481         }
13482         
13483         if (align ==='left' && this.fieldLabel.length) {
13484             cfg.cn = [
13485                 {
13486                     tag: 'label',
13487                     'for' :  id,
13488                     cls : 'control-label',
13489                     html : this.fieldLabel
13490                 },
13491                 {
13492                     cls : "",
13493                     cn: [
13494                         inputblock
13495                     ]
13496                 }
13497
13498             ];
13499             
13500             if(this.labelWidth > 12){
13501                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13502             }
13503
13504             if(this.labelWidth < 13 && this.labelmd == 0){
13505                 this.labelmd = this.labelWidth;
13506             }
13507
13508             if(this.labellg > 0){
13509                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13510                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13511             }
13512
13513             if(this.labelmd > 0){
13514                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13515                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13516             }
13517
13518             if(this.labelsm > 0){
13519                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13520                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13521             }
13522
13523             if(this.labelxs > 0){
13524                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13525                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13526             }
13527             
13528         } else if ( this.fieldLabel.length) {
13529             cfg.cn = [
13530
13531                {
13532                    tag: 'label',
13533                    //cls : 'input-group-addon',
13534                    html : this.fieldLabel
13535
13536                },
13537
13538                inputblock
13539
13540            ];
13541
13542         } else {
13543
13544             cfg.cn = [
13545
13546                 inputblock
13547
13548             ];
13549                 
13550         }
13551         
13552         if (this.disabled) {
13553             input.disabled=true;
13554         }
13555         
13556         return cfg;
13557         
13558     },
13559     /**
13560      * return the real textarea element.
13561      */
13562     inputEl: function ()
13563     {
13564         return this.el.select('textarea.form-control',true).first();
13565     },
13566     
13567     /**
13568      * Clear any invalid styles/messages for this field
13569      */
13570     clearInvalid : function()
13571     {
13572         
13573         if(!this.el || this.preventMark){ // not rendered
13574             return;
13575         }
13576         
13577         var label = this.el.select('label', true).first();
13578         var icon = this.el.select('i.fa-star', true).first();
13579         
13580         if(label && icon){
13581             icon.remove();
13582         }
13583         this.el.removeClass( this.validClass);
13584         this.inputEl().removeClass('is-invalid');
13585          
13586         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13587             
13588             var feedback = this.el.select('.form-control-feedback', true).first();
13589             
13590             if(feedback){
13591                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13592             }
13593             
13594         }
13595         
13596         this.fireEvent('valid', this);
13597     },
13598     
13599      /**
13600      * Mark this field as valid
13601      */
13602     markValid : function()
13603     {
13604         if(!this.el  || this.preventMark){ // not rendered
13605             return;
13606         }
13607         
13608         this.el.removeClass([this.invalidClass, this.validClass]);
13609         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13610         
13611         var feedback = this.el.select('.form-control-feedback', true).first();
13612             
13613         if(feedback){
13614             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13615         }
13616
13617         if(this.disabled || this.allowBlank){
13618             return;
13619         }
13620         
13621         var label = this.el.select('label', true).first();
13622         var icon = this.el.select('i.fa-star', true).first();
13623         
13624         if(label && icon){
13625             icon.remove();
13626         }
13627         if (Roo.bootstrap.version == 3) {
13628             this.el.addClass(this.validClass);
13629         } else {
13630             this.inputEl().addClass('is-valid');
13631         }
13632         
13633         
13634         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13635             
13636             var feedback = this.el.select('.form-control-feedback', true).first();
13637             
13638             if(feedback){
13639                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13640                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13641             }
13642             
13643         }
13644         
13645         this.fireEvent('valid', this);
13646     },
13647     
13648      /**
13649      * Mark this field as invalid
13650      * @param {String} msg The validation message
13651      */
13652     markInvalid : function(msg)
13653     {
13654         if(!this.el  || this.preventMark){ // not rendered
13655             return;
13656         }
13657         
13658         this.el.removeClass([this.invalidClass, this.validClass]);
13659         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13660         
13661         var feedback = this.el.select('.form-control-feedback', true).first();
13662             
13663         if(feedback){
13664             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13665         }
13666
13667         if(this.disabled || this.allowBlank){
13668             return;
13669         }
13670         
13671         var label = this.el.select('label', true).first();
13672         var icon = this.el.select('i.fa-star', true).first();
13673         
13674         if(!this.getValue().length && label && !icon){
13675             this.el.createChild({
13676                 tag : 'i',
13677                 cls : 'text-danger fa fa-lg fa-star',
13678                 tooltip : 'This field is required',
13679                 style : 'margin-right:5px;'
13680             }, label, true);
13681         }
13682         
13683         if (Roo.bootstrap.version == 3) {
13684             this.el.addClass(this.invalidClass);
13685         } else {
13686             this.inputEl().addClass('is-invalid');
13687         }
13688         
13689         // fixme ... this may be depricated need to test..
13690         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13691             
13692             var feedback = this.el.select('.form-control-feedback', true).first();
13693             
13694             if(feedback){
13695                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13696                 
13697                 if(this.getValue().length || this.forceFeedback){
13698                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13699                 }
13700                 
13701             }
13702             
13703         }
13704         
13705         this.fireEvent('invalid', this, msg);
13706     }
13707 });
13708
13709  
13710 /*
13711  * - LGPL
13712  *
13713  * trigger field - base class for combo..
13714  * 
13715  */
13716  
13717 /**
13718  * @class Roo.bootstrap.form.TriggerField
13719  * @extends Roo.bootstrap.form.Input
13720  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13721  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13722  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13723  * for which you can provide a custom implementation.  For example:
13724  * <pre><code>
13725 var trigger = new Roo.bootstrap.form.TriggerField();
13726 trigger.onTriggerClick = myTriggerFn;
13727 trigger.applyTo('my-field');
13728 </code></pre>
13729  *
13730  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13731  * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13732  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13733  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13734  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13735
13736  * @constructor
13737  * Create a new TriggerField.
13738  * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13739  * to the base TextField)
13740  */
13741 Roo.bootstrap.form.TriggerField = function(config){
13742     this.mimicing = false;
13743     Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13744 };
13745
13746 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input,  {
13747     /**
13748      * @cfg {String} triggerClass A CSS class to apply to the trigger
13749      */
13750      /**
13751      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13752      */
13753     hideTrigger:false,
13754
13755     /**
13756      * @cfg {Boolean} removable (true|false) special filter default false
13757      */
13758     removable : false,
13759     
13760     /** @cfg {Boolean} grow @hide */
13761     /** @cfg {Number} growMin @hide */
13762     /** @cfg {Number} growMax @hide */
13763
13764     /**
13765      * @hide 
13766      * @method
13767      */
13768     autoSize: Roo.emptyFn,
13769     // private
13770     monitorTab : true,
13771     // private
13772     deferHeight : true,
13773
13774     
13775     actionMode : 'wrap',
13776     
13777     caret : false,
13778     
13779     
13780     getAutoCreate : function(){
13781        
13782         var align = this.labelAlign || this.parentLabelAlign();
13783         
13784         var id = Roo.id();
13785         
13786         var cfg = {
13787             cls: 'form-group' //input-group
13788         };
13789         
13790         
13791         var input =  {
13792             tag: 'input',
13793             id : id,
13794             type : this.inputType,
13795             cls : 'form-control',
13796             autocomplete: 'new-password',
13797             placeholder : this.placeholder || '' 
13798             
13799         };
13800         if (this.name) {
13801             input.name = this.name;
13802         }
13803         if (this.size) {
13804             input.cls += ' input-' + this.size;
13805         }
13806         
13807         if (this.disabled) {
13808             input.disabled=true;
13809         }
13810         
13811         var inputblock = input;
13812         
13813         if(this.hasFeedback && !this.allowBlank){
13814             
13815             var feedback = {
13816                 tag: 'span',
13817                 cls: 'glyphicon form-control-feedback'
13818             };
13819             
13820             if(this.removable && !this.editable  ){
13821                 inputblock = {
13822                     cls : 'has-feedback',
13823                     cn :  [
13824                         inputblock,
13825                         {
13826                             tag: 'button',
13827                             html : 'x',
13828                             cls : 'roo-combo-removable-btn close'
13829                         },
13830                         feedback
13831                     ] 
13832                 };
13833             } else {
13834                 inputblock = {
13835                     cls : 'has-feedback',
13836                     cn :  [
13837                         inputblock,
13838                         feedback
13839                     ] 
13840                 };
13841             }
13842
13843         } else {
13844             if(this.removable && !this.editable ){
13845                 inputblock = {
13846                     cls : 'roo-removable',
13847                     cn :  [
13848                         inputblock,
13849                         {
13850                             tag: 'button',
13851                             html : 'x',
13852                             cls : 'roo-combo-removable-btn close'
13853                         }
13854                     ] 
13855                 };
13856             }
13857         }
13858         
13859         if (this.before || this.after) {
13860             
13861             inputblock = {
13862                 cls : 'input-group',
13863                 cn :  [] 
13864             };
13865             if (this.before) {
13866                 inputblock.cn.push({
13867                     tag :'span',
13868                     cls : 'input-group-addon input-group-prepend input-group-text',
13869                     html : this.before
13870                 });
13871             }
13872             
13873             inputblock.cn.push(input);
13874             
13875             if(this.hasFeedback && !this.allowBlank){
13876                 inputblock.cls += ' has-feedback';
13877                 inputblock.cn.push(feedback);
13878             }
13879             
13880             if (this.after) {
13881                 inputblock.cn.push({
13882                     tag :'span',
13883                     cls : 'input-group-addon input-group-append input-group-text',
13884                     html : this.after
13885                 });
13886             }
13887             
13888         };
13889         
13890       
13891         
13892         var ibwrap = inputblock;
13893         
13894         if(this.multiple){
13895             ibwrap = {
13896                 tag: 'ul',
13897                 cls: 'roo-select2-choices',
13898                 cn:[
13899                     {
13900                         tag: 'li',
13901                         cls: 'roo-select2-search-field',
13902                         cn: [
13903
13904                             inputblock
13905                         ]
13906                     }
13907                 ]
13908             };
13909                 
13910         }
13911         
13912         var combobox = {
13913             cls: 'roo-select2-container input-group',
13914             cn: [
13915                  {
13916                     tag: 'input',
13917                     type : 'hidden',
13918                     cls: 'form-hidden-field'
13919                 },
13920                 ibwrap
13921             ]
13922         };
13923         
13924         if(!this.multiple && this.showToggleBtn){
13925             
13926             var caret = {
13927                         tag: 'span',
13928                         cls: 'caret'
13929              };
13930             if (this.caret != false) {
13931                 caret = {
13932                      tag: 'i',
13933                      cls: 'fa fa-' + this.caret
13934                 };
13935                 
13936             }
13937             
13938             combobox.cn.push({
13939                 tag :'span',
13940                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13941                 cn : [
13942                     Roo.bootstrap.version == 3 ? caret : '',
13943                     {
13944                         tag: 'span',
13945                         cls: 'combobox-clear',
13946                         cn  : [
13947                             {
13948                                 tag : 'i',
13949                                 cls: 'icon-remove'
13950                             }
13951                         ]
13952                     }
13953                 ]
13954
13955             })
13956         }
13957         
13958         if(this.multiple){
13959             combobox.cls += ' roo-select2-container-multi';
13960         }
13961          var indicator = {
13962             tag : 'i',
13963             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13964             tooltip : 'This field is required'
13965         };
13966         if (Roo.bootstrap.version == 4) {
13967             indicator = {
13968                 tag : 'i',
13969                 style : 'display:none'
13970             };
13971         }
13972         
13973         
13974         if (align ==='left' && this.fieldLabel.length) {
13975             
13976             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13977
13978             cfg.cn = [
13979                 indicator,
13980                 {
13981                     tag: 'label',
13982                     'for' :  id,
13983                     cls : 'control-label',
13984                     html : this.fieldLabel
13985
13986                 },
13987                 {
13988                     cls : "", 
13989                     cn: [
13990                         combobox
13991                     ]
13992                 }
13993
13994             ];
13995             
13996             var labelCfg = cfg.cn[1];
13997             var contentCfg = cfg.cn[2];
13998             
13999             if(this.indicatorpos == 'right'){
14000                 cfg.cn = [
14001                     {
14002                         tag: 'label',
14003                         'for' :  id,
14004                         cls : 'control-label',
14005                         cn : [
14006                             {
14007                                 tag : 'span',
14008                                 html : this.fieldLabel
14009                             },
14010                             indicator
14011                         ]
14012                     },
14013                     {
14014                         cls : "", 
14015                         cn: [
14016                             combobox
14017                         ]
14018                     }
14019
14020                 ];
14021                 
14022                 labelCfg = cfg.cn[0];
14023                 contentCfg = cfg.cn[1];
14024             }
14025             
14026             if(this.labelWidth > 12){
14027                 labelCfg.style = "width: " + this.labelWidth + 'px';
14028             }
14029             
14030             if(this.labelWidth < 13 && this.labelmd == 0){
14031                 this.labelmd = this.labelWidth;
14032             }
14033             
14034             if(this.labellg > 0){
14035                 labelCfg.cls += ' col-lg-' + this.labellg;
14036                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14037             }
14038             
14039             if(this.labelmd > 0){
14040                 labelCfg.cls += ' col-md-' + this.labelmd;
14041                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14042             }
14043             
14044             if(this.labelsm > 0){
14045                 labelCfg.cls += ' col-sm-' + this.labelsm;
14046                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14047             }
14048             
14049             if(this.labelxs > 0){
14050                 labelCfg.cls += ' col-xs-' + this.labelxs;
14051                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14052             }
14053             
14054         } else if ( this.fieldLabel.length) {
14055 //                Roo.log(" label");
14056             cfg.cn = [
14057                 indicator,
14058                {
14059                    tag: 'label',
14060                    //cls : 'input-group-addon',
14061                    html : this.fieldLabel
14062
14063                },
14064
14065                combobox
14066
14067             ];
14068             
14069             if(this.indicatorpos == 'right'){
14070                 
14071                 cfg.cn = [
14072                     {
14073                        tag: 'label',
14074                        cn : [
14075                            {
14076                                tag : 'span',
14077                                html : this.fieldLabel
14078                            },
14079                            indicator
14080                        ]
14081
14082                     },
14083                     combobox
14084
14085                 ];
14086
14087             }
14088
14089         } else {
14090             
14091 //                Roo.log(" no label && no align");
14092                 cfg = combobox
14093                      
14094                 
14095         }
14096         
14097         var settings=this;
14098         ['xs','sm','md','lg'].map(function(size){
14099             if (settings[size]) {
14100                 cfg.cls += ' col-' + size + '-' + settings[size];
14101             }
14102         });
14103         
14104         return cfg;
14105         
14106     },
14107     
14108     
14109     
14110     // private
14111     onResize : function(w, h){
14112 //        Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14113 //        if(typeof w == 'number'){
14114 //            var x = w - this.trigger.getWidth();
14115 //            this.inputEl().setWidth(this.adjustWidth('input', x));
14116 //            this.trigger.setStyle('left', x+'px');
14117 //        }
14118     },
14119
14120     // private
14121     adjustSize : Roo.BoxComponent.prototype.adjustSize,
14122
14123     // private
14124     getResizeEl : function(){
14125         return this.inputEl();
14126     },
14127
14128     // private
14129     getPositionEl : function(){
14130         return this.inputEl();
14131     },
14132
14133     // private
14134     alignErrorIcon : function(){
14135         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14136     },
14137
14138     // private
14139     initEvents : function(){
14140         
14141         this.createList();
14142         
14143         Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14144         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14145         if(!this.multiple && this.showToggleBtn){
14146             this.trigger = this.el.select('span.dropdown-toggle',true).first();
14147             if(this.hideTrigger){
14148                 this.trigger.setDisplayed(false);
14149             }
14150             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14151         }
14152         
14153         if(this.multiple){
14154             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14155         }
14156         
14157         if(this.removable && !this.editable && !this.tickable){
14158             var close = this.closeTriggerEl();
14159             
14160             if(close){
14161                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14162                 close.on('click', this.removeBtnClick, this, close);
14163             }
14164         }
14165         
14166         //this.trigger.addClassOnOver('x-form-trigger-over');
14167         //this.trigger.addClassOnClick('x-form-trigger-click');
14168         
14169         //if(!this.width){
14170         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14171         //}
14172     },
14173     
14174     closeTriggerEl : function()
14175     {
14176         var close = this.el.select('.roo-combo-removable-btn', true).first();
14177         return close ? close : false;
14178     },
14179     
14180     removeBtnClick : function(e, h, el)
14181     {
14182         e.preventDefault();
14183         
14184         if(this.fireEvent("remove", this) !== false){
14185             this.reset();
14186             this.fireEvent("afterremove", this)
14187         }
14188     },
14189     
14190     createList : function()
14191     {
14192         this.list = Roo.get(document.body).createChild({
14193             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14194             cls: 'typeahead typeahead-long dropdown-menu shadow',
14195             style: 'display:none'
14196         });
14197         
14198         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14199         
14200     },
14201
14202     // private
14203     initTrigger : function(){
14204        
14205     },
14206
14207     // private
14208     onDestroy : function(){
14209         if(this.trigger){
14210             this.trigger.removeAllListeners();
14211           //  this.trigger.remove();
14212         }
14213         //if(this.wrap){
14214         //    this.wrap.remove();
14215         //}
14216         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14217     },
14218
14219     // private
14220     onFocus : function(){
14221         Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14222         /*
14223         if(!this.mimicing){
14224             this.wrap.addClass('x-trigger-wrap-focus');
14225             this.mimicing = true;
14226             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14227             if(this.monitorTab){
14228                 this.el.on("keydown", this.checkTab, this);
14229             }
14230         }
14231         */
14232     },
14233
14234     // private
14235     checkTab : function(e){
14236         if(e.getKey() == e.TAB){
14237             this.triggerBlur();
14238         }
14239     },
14240
14241     // private
14242     onBlur : function(){
14243         // do nothing
14244     },
14245
14246     // private
14247     mimicBlur : function(e, t){
14248         /*
14249         if(!this.wrap.contains(t) && this.validateBlur()){
14250             this.triggerBlur();
14251         }
14252         */
14253     },
14254
14255     // private
14256     triggerBlur : function(){
14257         this.mimicing = false;
14258         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14259         if(this.monitorTab){
14260             this.el.un("keydown", this.checkTab, this);
14261         }
14262         //this.wrap.removeClass('x-trigger-wrap-focus');
14263         Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14264     },
14265
14266     // private
14267     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14268     validateBlur : function(e, t){
14269         return true;
14270     },
14271
14272     // private
14273     onDisable : function(){
14274         this.inputEl().dom.disabled = true;
14275         //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14276         //if(this.wrap){
14277         //    this.wrap.addClass('x-item-disabled');
14278         //}
14279     },
14280
14281     // private
14282     onEnable : function(){
14283         this.inputEl().dom.disabled = false;
14284         //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14285         //if(this.wrap){
14286         //    this.el.removeClass('x-item-disabled');
14287         //}
14288     },
14289
14290     // private
14291     onShow : function(){
14292         var ae = this.getActionEl();
14293         
14294         if(ae){
14295             ae.dom.style.display = '';
14296             ae.dom.style.visibility = 'visible';
14297         }
14298     },
14299
14300     // private
14301     
14302     onHide : function(){
14303         var ae = this.getActionEl();
14304         ae.dom.style.display = 'none';
14305     },
14306
14307     /**
14308      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
14309      * by an implementing function.
14310      * @method
14311      * @param {EventObject} e
14312      */
14313     onTriggerClick : Roo.emptyFn
14314 });
14315  
14316 /*
14317 * Licence: LGPL
14318 */
14319
14320 /**
14321  * @class Roo.bootstrap.form.CardUploader
14322  * @extends Roo.bootstrap.Button
14323  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14324  * @cfg {Number} errorTimeout default 3000
14325  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
14326  * @cfg {Array}  html The button text.
14327
14328  *
14329  * @constructor
14330  * Create a new CardUploader
14331  * @param {Object} config The config object
14332  */
14333
14334 Roo.bootstrap.form.CardUploader = function(config){
14335     
14336  
14337     
14338     Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14339     
14340     
14341     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
14342         return r.data.id
14343      });
14344     
14345      this.addEvents({
14346          // raw events
14347         /**
14348          * @event preview
14349          * When a image is clicked on - and needs to display a slideshow or similar..
14350          * @param {Roo.bootstrap.Card} this
14351          * @param {Object} The image information data 
14352          *
14353          */
14354         'preview' : true,
14355          /**
14356          * @event download
14357          * When a the download link is clicked
14358          * @param {Roo.bootstrap.Card} this
14359          * @param {Object} The image information data  contains 
14360          */
14361         'download' : true
14362         
14363     });
14364 };
14365  
14366 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input,  {
14367     
14368      
14369     errorTimeout : 3000,
14370      
14371     images : false,
14372    
14373     fileCollection : false,
14374     allowBlank : true,
14375     
14376     getAutoCreate : function()
14377     {
14378         
14379         var cfg =  {
14380             cls :'form-group' ,
14381             cn : [
14382                
14383                 {
14384                     tag: 'label',
14385                    //cls : 'input-group-addon',
14386                     html : this.fieldLabel
14387
14388                 },
14389
14390                 {
14391                     tag: 'input',
14392                     type : 'hidden',
14393                     name : this.name,
14394                     value : this.value,
14395                     cls : 'd-none  form-control'
14396                 },
14397                 
14398                 {
14399                     tag: 'input',
14400                     multiple : 'multiple',
14401                     type : 'file',
14402                     cls : 'd-none  roo-card-upload-selector'
14403                 },
14404                 
14405                 {
14406                     cls : 'roo-card-uploader-button-container w-100 mb-2'
14407                 },
14408                 {
14409                     cls : 'card-columns roo-card-uploader-container'
14410                 }
14411
14412             ]
14413         };
14414            
14415          
14416         return cfg;
14417     },
14418     
14419     getChildContainer : function() /// what children are added to.
14420     {
14421         return this.containerEl;
14422     },
14423    
14424     getButtonContainer : function() /// what children are added to.
14425     {
14426         return this.el.select(".roo-card-uploader-button-container").first();
14427     },
14428    
14429     initEvents : function()
14430     {
14431         
14432         Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14433         
14434         var t = this;
14435         this.addxtype({
14436             xns: Roo.bootstrap,
14437
14438             xtype : 'Button',
14439             container_method : 'getButtonContainer' ,            
14440             html :  this.html, // fix changable?
14441             cls : 'w-100 ',
14442             listeners : {
14443                 'click' : function(btn, e) {
14444                     t.onClick(e);
14445                 }
14446             }
14447         });
14448         
14449         
14450         
14451         
14452         this.urlAPI = (window.createObjectURL && window) || 
14453                                 (window.URL && URL.revokeObjectURL && URL) || 
14454                                 (window.webkitURL && webkitURL);
14455                         
14456          
14457          
14458          
14459         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14460         
14461         this.selectorEl.on('change', this.onFileSelected, this);
14462         if (this.images) {
14463             var t = this;
14464             this.images.forEach(function(img) {
14465                 t.addCard(img)
14466             });
14467             this.images = false;
14468         }
14469         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14470          
14471        
14472     },
14473     
14474    
14475     onClick : function(e)
14476     {
14477         e.preventDefault();
14478          
14479         this.selectorEl.dom.click();
14480          
14481     },
14482     
14483     onFileSelected : function(e)
14484     {
14485         e.preventDefault();
14486         
14487         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14488             return;
14489         }
14490         
14491         Roo.each(this.selectorEl.dom.files, function(file){    
14492             this.addFile(file);
14493         }, this);
14494          
14495     },
14496     
14497       
14498     
14499       
14500     
14501     addFile : function(file)
14502     {
14503            
14504         if(typeof(file) === 'string'){
14505             throw "Add file by name?"; // should not happen
14506             return;
14507         }
14508         
14509         if(!file || !this.urlAPI){
14510             return;
14511         }
14512         
14513         // file;
14514         // file.type;
14515         
14516         var _this = this;
14517         
14518         
14519         var url = _this.urlAPI.createObjectURL( file);
14520            
14521         this.addCard({
14522             id : Roo.bootstrap.form.CardUploader.ID--,
14523             is_uploaded : false,
14524             src : url,
14525             srcfile : file,
14526             title : file.name,
14527             mimetype : file.type,
14528             preview : false,
14529             is_deleted : 0
14530         });
14531         
14532     },
14533     
14534     /**
14535      * addCard - add an Attachment to the uploader
14536      * @param data - the data about the image to upload
14537      *
14538      * {
14539           id : 123
14540           title : "Title of file",
14541           is_uploaded : false,
14542           src : "http://.....",
14543           srcfile : { the File upload object },
14544           mimetype : file.type,
14545           preview : false,
14546           is_deleted : 0
14547           .. any other data...
14548         }
14549      *
14550      * 
14551     */
14552     
14553     addCard : function (data)
14554     {
14555         // hidden input element?
14556         // if the file is not an image...
14557         //then we need to use something other that and header_image
14558         var t = this;
14559         //   remove.....
14560         var footer = [
14561             {
14562                 xns : Roo.bootstrap,
14563                 xtype : 'CardFooter',
14564                  items: [
14565                     {
14566                         xns : Roo.bootstrap,
14567                         xtype : 'Element',
14568                         cls : 'd-flex',
14569                         items : [
14570                             
14571                             {
14572                                 xns : Roo.bootstrap,
14573                                 xtype : 'Button',
14574                                 html : String.format("<small>{0}</small>", data.title),
14575                                 cls : 'col-10 text-left',
14576                                 size: 'sm',
14577                                 weight: 'link',
14578                                 fa : 'download',
14579                                 listeners : {
14580                                     click : function() {
14581                                      
14582                                         t.fireEvent( "download", t, data );
14583                                     }
14584                                 }
14585                             },
14586                           
14587                             {
14588                                 xns : Roo.bootstrap,
14589                                 xtype : 'Button',
14590                                 style: 'max-height: 28px; ',
14591                                 size : 'sm',
14592                                 weight: 'danger',
14593                                 cls : 'col-2',
14594                                 fa : 'times',
14595                                 listeners : {
14596                                     click : function() {
14597                                         t.removeCard(data.id)
14598                                     }
14599                                 }
14600                             }
14601                         ]
14602                     }
14603                     
14604                 ] 
14605             }
14606             
14607         ];
14608         
14609         var cn = this.addxtype(
14610             {
14611                  
14612                 xns : Roo.bootstrap,
14613                 xtype : 'Card',
14614                 closeable : true,
14615                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14616                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14617                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14618                 data : data,
14619                 html : false,
14620                  
14621                 items : footer,
14622                 initEvents : function() {
14623                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14624                     var card = this;
14625                     this.imgEl = this.el.select('.card-img-top').first();
14626                     if (this.imgEl) {
14627                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14628                         this.imgEl.set({ 'pointer' : 'cursor' });
14629                                   
14630                     }
14631                     this.getCardFooter().addClass('p-1');
14632                     
14633                   
14634                 }
14635                 
14636             }
14637         );
14638         // dont' really need ot update items.
14639         // this.items.push(cn);
14640         this.fileCollection.add(cn);
14641         
14642         if (!data.srcfile) {
14643             this.updateInput();
14644             return;
14645         }
14646             
14647         var _t = this;
14648         var reader = new FileReader();
14649         reader.addEventListener("load", function() {  
14650             data.srcdata =  reader.result;
14651             _t.updateInput();
14652         });
14653         reader.readAsDataURL(data.srcfile);
14654         
14655         
14656         
14657     },
14658     removeCard : function(id)
14659     {
14660         
14661         var card  = this.fileCollection.get(id);
14662         card.data.is_deleted = 1;
14663         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14664         //this.fileCollection.remove(card);
14665         //this.items = this.items.filter(function(e) { return e != card });
14666         // dont' really need ot update items.
14667         card.el.dom.parentNode.removeChild(card.el.dom);
14668         this.updateInput();
14669
14670         
14671     },
14672     reset: function()
14673     {
14674         this.fileCollection.each(function(card) {
14675             if (card.el.dom && card.el.dom.parentNode) {
14676                 card.el.dom.parentNode.removeChild(card.el.dom);
14677             }
14678         });
14679         this.fileCollection.clear();
14680         this.updateInput();
14681     },
14682     
14683     updateInput : function()
14684     {
14685          var data = [];
14686         this.fileCollection.each(function(e) {
14687             data.push(e.data);
14688             
14689         });
14690         this.inputEl().dom.value = JSON.stringify(data);
14691         
14692         
14693         
14694     }
14695     
14696     
14697 });
14698
14699
14700 Roo.bootstrap.form.CardUploader.ID = -1;/*
14701  * Based on:
14702  * Ext JS Library 1.1.1
14703  * Copyright(c) 2006-2007, Ext JS, LLC.
14704  *
14705  * Originally Released Under LGPL - original licence link has changed is not relivant.
14706  *
14707  * Fork - LGPL
14708  * <script type="text/javascript">
14709  */
14710
14711
14712 /**
14713  * @class Roo.data.SortTypes
14714  * @static
14715  * Defines the default sorting (casting?) comparison functions used when sorting data.
14716  */
14717 Roo.data.SortTypes = {
14718     /**
14719      * Default sort that does nothing
14720      * @param {Mixed} s The value being converted
14721      * @return {Mixed} The comparison value
14722      */
14723     none : function(s){
14724         return s;
14725     },
14726     
14727     /**
14728      * The regular expression used to strip tags
14729      * @type {RegExp}
14730      * @property
14731      */
14732     stripTagsRE : /<\/?[^>]+>/gi,
14733     
14734     /**
14735      * Strips all HTML tags to sort on text only
14736      * @param {Mixed} s The value being converted
14737      * @return {String} The comparison value
14738      */
14739     asText : function(s){
14740         return String(s).replace(this.stripTagsRE, "");
14741     },
14742     
14743     /**
14744      * Strips all HTML tags to sort on text only - Case insensitive
14745      * @param {Mixed} s The value being converted
14746      * @return {String} The comparison value
14747      */
14748     asUCText : function(s){
14749         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14750     },
14751     
14752     /**
14753      * Case insensitive string
14754      * @param {Mixed} s The value being converted
14755      * @return {String} The comparison value
14756      */
14757     asUCString : function(s) {
14758         return String(s).toUpperCase();
14759     },
14760     
14761     /**
14762      * Date sorting
14763      * @param {Mixed} s The value being converted
14764      * @return {Number} The comparison value
14765      */
14766     asDate : function(s) {
14767         if(!s){
14768             return 0;
14769         }
14770         if(s instanceof Date){
14771             return s.getTime();
14772         }
14773         return Date.parse(String(s));
14774     },
14775     
14776     /**
14777      * Float sorting
14778      * @param {Mixed} s The value being converted
14779      * @return {Float} The comparison value
14780      */
14781     asFloat : function(s) {
14782         var val = parseFloat(String(s).replace(/,/g, ""));
14783         if(isNaN(val)) {
14784             val = 0;
14785         }
14786         return val;
14787     },
14788     
14789     /**
14790      * Integer sorting
14791      * @param {Mixed} s The value being converted
14792      * @return {Number} The comparison value
14793      */
14794     asInt : function(s) {
14795         var val = parseInt(String(s).replace(/,/g, ""));
14796         if(isNaN(val)) {
14797             val = 0;
14798         }
14799         return val;
14800     }
14801 };/*
14802  * Based on:
14803  * Ext JS Library 1.1.1
14804  * Copyright(c) 2006-2007, Ext JS, LLC.
14805  *
14806  * Originally Released Under LGPL - original licence link has changed is not relivant.
14807  *
14808  * Fork - LGPL
14809  * <script type="text/javascript">
14810  */
14811
14812 /**
14813 * @class Roo.data.Record
14814  * Instances of this class encapsulate both record <em>definition</em> information, and record
14815  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14816  * to access Records cached in an {@link Roo.data.Store} object.<br>
14817  * <p>
14818  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14819  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14820  * objects.<br>
14821  * <p>
14822  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14823  * @constructor
14824  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14825  * {@link #create}. The parameters are the same.
14826  * @param {Array} data An associative Array of data values keyed by the field name.
14827  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14828  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14829  * not specified an integer id is generated.
14830  */
14831 Roo.data.Record = function(data, id){
14832     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14833     this.data = data;
14834 };
14835
14836 /**
14837  * Generate a constructor for a specific record layout.
14838  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14839  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14840  * Each field definition object may contain the following properties: <ul>
14841  * <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,
14842  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14843  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14844  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14845  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14846  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14847  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14848  * this may be omitted.</p></li>
14849  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14850  * <ul><li>auto (Default, implies no conversion)</li>
14851  * <li>string</li>
14852  * <li>int</li>
14853  * <li>float</li>
14854  * <li>boolean</li>
14855  * <li>date</li></ul></p></li>
14856  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14857  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14858  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14859  * by the Reader into an object that will be stored in the Record. It is passed the
14860  * following parameters:<ul>
14861  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14862  * </ul></p></li>
14863  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14864  * </ul>
14865  * <br>usage:<br><pre><code>
14866 var TopicRecord = Roo.data.Record.create(
14867     {name: 'title', mapping: 'topic_title'},
14868     {name: 'author', mapping: 'username'},
14869     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14870     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14871     {name: 'lastPoster', mapping: 'user2'},
14872     {name: 'excerpt', mapping: 'post_text'}
14873 );
14874
14875 var myNewRecord = new TopicRecord({
14876     title: 'Do my job please',
14877     author: 'noobie',
14878     totalPosts: 1,
14879     lastPost: new Date(),
14880     lastPoster: 'Animal',
14881     excerpt: 'No way dude!'
14882 });
14883 myStore.add(myNewRecord);
14884 </code></pre>
14885  * @method create
14886  * @static
14887  */
14888 Roo.data.Record.create = function(o){
14889     var f = function(){
14890         f.superclass.constructor.apply(this, arguments);
14891     };
14892     Roo.extend(f, Roo.data.Record);
14893     var p = f.prototype;
14894     p.fields = new Roo.util.MixedCollection(false, function(field){
14895         return field.name;
14896     });
14897     for(var i = 0, len = o.length; i < len; i++){
14898         p.fields.add(new Roo.data.Field(o[i]));
14899     }
14900     f.getField = function(name){
14901         return p.fields.get(name);  
14902     };
14903     return f;
14904 };
14905
14906 Roo.data.Record.AUTO_ID = 1000;
14907 Roo.data.Record.EDIT = 'edit';
14908 Roo.data.Record.REJECT = 'reject';
14909 Roo.data.Record.COMMIT = 'commit';
14910
14911 Roo.data.Record.prototype = {
14912     /**
14913      * Readonly flag - true if this record has been modified.
14914      * @type Boolean
14915      */
14916     dirty : false,
14917     editing : false,
14918     error: null,
14919     modified: null,
14920
14921     // private
14922     join : function(store){
14923         this.store = store;
14924     },
14925
14926     /**
14927      * Set the named field to the specified value.
14928      * @param {String} name The name of the field to set.
14929      * @param {Object} value The value to set the field to.
14930      */
14931     set : function(name, value){
14932         if(this.data[name] == value){
14933             return;
14934         }
14935         this.dirty = true;
14936         if(!this.modified){
14937             this.modified = {};
14938         }
14939         if(typeof this.modified[name] == 'undefined'){
14940             this.modified[name] = this.data[name];
14941         }
14942         this.data[name] = value;
14943         if(!this.editing && this.store){
14944             this.store.afterEdit(this);
14945         }       
14946     },
14947
14948     /**
14949      * Get the value of the named field.
14950      * @param {String} name The name of the field to get the value of.
14951      * @return {Object} The value of the field.
14952      */
14953     get : function(name){
14954         return this.data[name]; 
14955     },
14956
14957     // private
14958     beginEdit : function(){
14959         this.editing = true;
14960         this.modified = {}; 
14961     },
14962
14963     // private
14964     cancelEdit : function(){
14965         this.editing = false;
14966         delete this.modified;
14967     },
14968
14969     // private
14970     endEdit : function(){
14971         this.editing = false;
14972         if(this.dirty && this.store){
14973             this.store.afterEdit(this);
14974         }
14975     },
14976
14977     /**
14978      * Usually called by the {@link Roo.data.Store} which owns the Record.
14979      * Rejects all changes made to the Record since either creation, or the last commit operation.
14980      * Modified fields are reverted to their original values.
14981      * <p>
14982      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14983      * of reject operations.
14984      */
14985     reject : function(){
14986         var m = this.modified;
14987         for(var n in m){
14988             if(typeof m[n] != "function"){
14989                 this.data[n] = m[n];
14990             }
14991         }
14992         this.dirty = false;
14993         delete this.modified;
14994         this.editing = false;
14995         if(this.store){
14996             this.store.afterReject(this);
14997         }
14998     },
14999
15000     /**
15001      * Usually called by the {@link Roo.data.Store} which owns the Record.
15002      * Commits all changes made to the Record since either creation, or the last commit operation.
15003      * <p>
15004      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15005      * of commit operations.
15006      */
15007     commit : function(){
15008         this.dirty = false;
15009         delete this.modified;
15010         this.editing = false;
15011         if(this.store){
15012             this.store.afterCommit(this);
15013         }
15014     },
15015
15016     // private
15017     hasError : function(){
15018         return this.error != null;
15019     },
15020
15021     // private
15022     clearError : function(){
15023         this.error = null;
15024     },
15025
15026     /**
15027      * Creates a copy of this record.
15028      * @param {String} id (optional) A new record id if you don't want to use this record's id
15029      * @return {Record}
15030      */
15031     copy : function(newId) {
15032         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15033     }
15034 };/*
15035  * Based on:
15036  * Ext JS Library 1.1.1
15037  * Copyright(c) 2006-2007, Ext JS, LLC.
15038  *
15039  * Originally Released Under LGPL - original licence link has changed is not relivant.
15040  *
15041  * Fork - LGPL
15042  * <script type="text/javascript">
15043  */
15044
15045
15046
15047 /**
15048  * @class Roo.data.Store
15049  * @extends Roo.util.Observable
15050  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15051  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15052  * <p>
15053  * 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
15054  * has no knowledge of the format of the data returned by the Proxy.<br>
15055  * <p>
15056  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15057  * instances from the data object. These records are cached and made available through accessor functions.
15058  * @constructor
15059  * Creates a new Store.
15060  * @param {Object} config A config object containing the objects needed for the Store to access data,
15061  * and read the data into Records.
15062  */
15063 Roo.data.Store = function(config){
15064     this.data = new Roo.util.MixedCollection(false);
15065     this.data.getKey = function(o){
15066         return o.id;
15067     };
15068     this.baseParams = {};
15069     // private
15070     this.paramNames = {
15071         "start" : "start",
15072         "limit" : "limit",
15073         "sort" : "sort",
15074         "dir" : "dir",
15075         "multisort" : "_multisort"
15076     };
15077
15078     if(config && config.data){
15079         this.inlineData = config.data;
15080         delete config.data;
15081     }
15082
15083     Roo.apply(this, config);
15084     
15085     if(this.reader){ // reader passed
15086         this.reader = Roo.factory(this.reader, Roo.data);
15087         this.reader.xmodule = this.xmodule || false;
15088         if(!this.recordType){
15089             this.recordType = this.reader.recordType;
15090         }
15091         if(this.reader.onMetaChange){
15092             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15093         }
15094     }
15095
15096     if(this.recordType){
15097         this.fields = this.recordType.prototype.fields;
15098     }
15099     this.modified = [];
15100
15101     this.addEvents({
15102         /**
15103          * @event datachanged
15104          * Fires when the data cache has changed, and a widget which is using this Store
15105          * as a Record cache should refresh its view.
15106          * @param {Store} this
15107          */
15108         datachanged : true,
15109         /**
15110          * @event metachange
15111          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15112          * @param {Store} this
15113          * @param {Object} meta The JSON metadata
15114          */
15115         metachange : true,
15116         /**
15117          * @event add
15118          * Fires when Records have been added to the Store
15119          * @param {Store} this
15120          * @param {Roo.data.Record[]} records The array of Records added
15121          * @param {Number} index The index at which the record(s) were added
15122          */
15123         add : true,
15124         /**
15125          * @event remove
15126          * Fires when a Record has been removed from the Store
15127          * @param {Store} this
15128          * @param {Roo.data.Record} record The Record that was removed
15129          * @param {Number} index The index at which the record was removed
15130          */
15131         remove : true,
15132         /**
15133          * @event update
15134          * Fires when a Record has been updated
15135          * @param {Store} this
15136          * @param {Roo.data.Record} record The Record that was updated
15137          * @param {String} operation The update operation being performed.  Value may be one of:
15138          * <pre><code>
15139  Roo.data.Record.EDIT
15140  Roo.data.Record.REJECT
15141  Roo.data.Record.COMMIT
15142          * </code></pre>
15143          */
15144         update : true,
15145         /**
15146          * @event clear
15147          * Fires when the data cache has been cleared.
15148          * @param {Store} this
15149          */
15150         clear : true,
15151         /**
15152          * @event beforeload
15153          * Fires before a request is made for a new data object.  If the beforeload handler returns false
15154          * the load action will be canceled.
15155          * @param {Store} this
15156          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15157          */
15158         beforeload : true,
15159         /**
15160          * @event beforeloadadd
15161          * Fires after a new set of Records has been loaded.
15162          * @param {Store} this
15163          * @param {Roo.data.Record[]} records The Records that were loaded
15164          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15165          */
15166         beforeloadadd : true,
15167         /**
15168          * @event load
15169          * Fires after a new set of Records has been loaded, before they are added to the store.
15170          * @param {Store} this
15171          * @param {Roo.data.Record[]} records The Records that were loaded
15172          * @param {Object} options The loading options that were specified (see {@link #load} for details)
15173          * @params {Object} return from reader
15174          */
15175         load : true,
15176         /**
15177          * @event loadexception
15178          * Fires if an exception occurs in the Proxy during loading.
15179          * Called with the signature of the Proxy's "loadexception" event.
15180          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15181          * 
15182          * @param {Proxy} 
15183          * @param {Object} return from JsonData.reader() - success, totalRecords, records
15184          * @param {Object} load options 
15185          * @param {Object} jsonData from your request (normally this contains the Exception)
15186          */
15187         loadexception : true
15188     });
15189     
15190     if(this.proxy){
15191         this.proxy = Roo.factory(this.proxy, Roo.data);
15192         this.proxy.xmodule = this.xmodule || false;
15193         this.relayEvents(this.proxy,  ["loadexception"]);
15194     }
15195     this.sortToggle = {};
15196     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15197
15198     Roo.data.Store.superclass.constructor.call(this);
15199
15200     if(this.inlineData){
15201         this.loadData(this.inlineData);
15202         delete this.inlineData;
15203     }
15204 };
15205
15206 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15207      /**
15208     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
15209     * without a remote query - used by combo/forms at present.
15210     */
15211     
15212     /**
15213     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15214     */
15215     /**
15216     * @cfg {Array} data Inline data to be loaded when the store is initialized.
15217     */
15218     /**
15219     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
15220     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15221     */
15222     /**
15223     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15224     * on any HTTP request
15225     */
15226     /**
15227     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15228     */
15229     /**
15230     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15231     */
15232     multiSort: false,
15233     /**
15234     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15235     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15236     */
15237     remoteSort : false,
15238
15239     /**
15240     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15241      * loaded or when a record is removed. (defaults to false).
15242     */
15243     pruneModifiedRecords : false,
15244
15245     // private
15246     lastOptions : null,
15247
15248     /**
15249      * Add Records to the Store and fires the add event.
15250      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15251      */
15252     add : function(records){
15253         records = [].concat(records);
15254         for(var i = 0, len = records.length; i < len; i++){
15255             records[i].join(this);
15256         }
15257         var index = this.data.length;
15258         this.data.addAll(records);
15259         this.fireEvent("add", this, records, index);
15260     },
15261
15262     /**
15263      * Remove a Record from the Store and fires the remove event.
15264      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15265      */
15266     remove : function(record){
15267         var index = this.data.indexOf(record);
15268         this.data.removeAt(index);
15269  
15270         if(this.pruneModifiedRecords){
15271             this.modified.remove(record);
15272         }
15273         this.fireEvent("remove", this, record, index);
15274     },
15275
15276     /**
15277      * Remove all Records from the Store and fires the clear event.
15278      */
15279     removeAll : function(){
15280         this.data.clear();
15281         if(this.pruneModifiedRecords){
15282             this.modified = [];
15283         }
15284         this.fireEvent("clear", this);
15285     },
15286
15287     /**
15288      * Inserts Records to the Store at the given index and fires the add event.
15289      * @param {Number} index The start index at which to insert the passed Records.
15290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15291      */
15292     insert : function(index, records){
15293         records = [].concat(records);
15294         for(var i = 0, len = records.length; i < len; i++){
15295             this.data.insert(index, records[i]);
15296             records[i].join(this);
15297         }
15298         this.fireEvent("add", this, records, index);
15299     },
15300
15301     /**
15302      * Get the index within the cache of the passed Record.
15303      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15304      * @return {Number} The index of the passed Record. Returns -1 if not found.
15305      */
15306     indexOf : function(record){
15307         return this.data.indexOf(record);
15308     },
15309
15310     /**
15311      * Get the index within the cache of the Record with the passed id.
15312      * @param {String} id The id of the Record to find.
15313      * @return {Number} The index of the Record. Returns -1 if not found.
15314      */
15315     indexOfId : function(id){
15316         return this.data.indexOfKey(id);
15317     },
15318
15319     /**
15320      * Get the Record with the specified id.
15321      * @param {String} id The id of the Record to find.
15322      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15323      */
15324     getById : function(id){
15325         return this.data.key(id);
15326     },
15327
15328     /**
15329      * Get the Record at the specified index.
15330      * @param {Number} index The index of the Record to find.
15331      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15332      */
15333     getAt : function(index){
15334         return this.data.itemAt(index);
15335     },
15336
15337     /**
15338      * Returns a range of Records between specified indices.
15339      * @param {Number} startIndex (optional) The starting index (defaults to 0)
15340      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15341      * @return {Roo.data.Record[]} An array of Records
15342      */
15343     getRange : function(start, end){
15344         return this.data.getRange(start, end);
15345     },
15346
15347     // private
15348     storeOptions : function(o){
15349         o = Roo.apply({}, o);
15350         delete o.callback;
15351         delete o.scope;
15352         this.lastOptions = o;
15353     },
15354
15355     /**
15356      * Loads the Record cache from the configured Proxy using the configured Reader.
15357      * <p>
15358      * If using remote paging, then the first load call must specify the <em>start</em>
15359      * and <em>limit</em> properties in the options.params property to establish the initial
15360      * position within the dataset, and the number of Records to cache on each read from the Proxy.
15361      * <p>
15362      * <strong>It is important to note that for remote data sources, loading is asynchronous,
15363      * and this call will return before the new data has been loaded. Perform any post-processing
15364      * in a callback function, or in a "load" event handler.</strong>
15365      * <p>
15366      * @param {Object} options An object containing properties which control loading options:<ul>
15367      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15368      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15369      * <pre>
15370                 {
15371                     data : data,  // array of key=>value data like JsonReader
15372                     total : data.length,
15373                     success : true
15374                     
15375                 }
15376         </pre>
15377             }.</li>
15378      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15379      * passed the following arguments:<ul>
15380      * <li>r : Roo.data.Record[]</li>
15381      * <li>options: Options object from the load call</li>
15382      * <li>success: Boolean success indicator</li></ul></li>
15383      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15384      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15385      * </ul>
15386      */
15387     load : function(options){
15388         options = options || {};
15389         if(this.fireEvent("beforeload", this, options) !== false){
15390             this.storeOptions(options);
15391             var p = Roo.apply(options.params || {}, this.baseParams);
15392             // if meta was not loaded from remote source.. try requesting it.
15393             if (!this.reader.metaFromRemote) {
15394                 p._requestMeta = 1;
15395             }
15396             if(this.sortInfo && this.remoteSort){
15397                 var pn = this.paramNames;
15398                 p[pn["sort"]] = this.sortInfo.field;
15399                 p[pn["dir"]] = this.sortInfo.direction;
15400             }
15401             if (this.multiSort) {
15402                 var pn = this.paramNames;
15403                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15404             }
15405             
15406             this.proxy.load(p, this.reader, this.loadRecords, this, options);
15407         }
15408     },
15409
15410     /**
15411      * Reloads the Record cache from the configured Proxy using the configured Reader and
15412      * the options from the last load operation performed.
15413      * @param {Object} options (optional) An object containing properties which may override the options
15414      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15415      * the most recently used options are reused).
15416      */
15417     reload : function(options){
15418         this.load(Roo.applyIf(options||{}, this.lastOptions));
15419     },
15420
15421     // private
15422     // Called as a callback by the Reader during a load operation.
15423     loadRecords : function(o, options, success){
15424          
15425         if(!o){
15426             if(success !== false){
15427                 this.fireEvent("load", this, [], options, o);
15428             }
15429             if(options.callback){
15430                 options.callback.call(options.scope || this, [], options, false);
15431             }
15432             return;
15433         }
15434         // if data returned failure - throw an exception.
15435         if (o.success === false) {
15436             // show a message if no listener is registered.
15437             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15438                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15439             }
15440             // loadmask wil be hooked into this..
15441             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15442             return;
15443         }
15444         var r = o.records, t = o.totalRecords || r.length;
15445         
15446         this.fireEvent("beforeloadadd", this, r, options, o);
15447         
15448         if(!options || options.add !== true){
15449             if(this.pruneModifiedRecords){
15450                 this.modified = [];
15451             }
15452             for(var i = 0, len = r.length; i < len; i++){
15453                 r[i].join(this);
15454             }
15455             if(this.snapshot){
15456                 this.data = this.snapshot;
15457                 delete this.snapshot;
15458             }
15459             this.data.clear();
15460             this.data.addAll(r);
15461             this.totalLength = t;
15462             this.applySort();
15463             this.fireEvent("datachanged", this);
15464         }else{
15465             this.totalLength = Math.max(t, this.data.length+r.length);
15466             this.add(r);
15467         }
15468         
15469         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15470                 
15471             var e = new Roo.data.Record({});
15472
15473             e.set(this.parent.displayField, this.parent.emptyTitle);
15474             e.set(this.parent.valueField, '');
15475
15476             this.insert(0, e);
15477         }
15478             
15479         this.fireEvent("load", this, r, options, o);
15480         if(options.callback){
15481             options.callback.call(options.scope || this, r, options, true);
15482         }
15483     },
15484
15485
15486     /**
15487      * Loads data from a passed data block. A Reader which understands the format of the data
15488      * must have been configured in the constructor.
15489      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15490      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15491      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15492      */
15493     loadData : function(o, append){
15494         var r = this.reader.readRecords(o);
15495         this.loadRecords(r, {add: append}, true);
15496     },
15497     
15498      /**
15499      * using 'cn' the nested child reader read the child array into it's child stores.
15500      * @param {Object} rec The record with a 'children array
15501      */
15502     loadDataFromChildren : function(rec)
15503     {
15504         this.loadData(this.reader.toLoadData(rec));
15505     },
15506     
15507
15508     /**
15509      * Gets the number of cached records.
15510      * <p>
15511      * <em>If using paging, this may not be the total size of the dataset. If the data object
15512      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15513      * the data set size</em>
15514      */
15515     getCount : function(){
15516         return this.data.length || 0;
15517     },
15518
15519     /**
15520      * Gets the total number of records in the dataset as returned by the server.
15521      * <p>
15522      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15523      * the dataset size</em>
15524      */
15525     getTotalCount : function(){
15526         return this.totalLength || 0;
15527     },
15528
15529     /**
15530      * Returns the sort state of the Store as an object with two properties:
15531      * <pre><code>
15532  field {String} The name of the field by which the Records are sorted
15533  direction {String} The sort order, "ASC" or "DESC"
15534      * </code></pre>
15535      */
15536     getSortState : function(){
15537         return this.sortInfo;
15538     },
15539
15540     // private
15541     applySort : function(){
15542         if(this.sortInfo && !this.remoteSort){
15543             var s = this.sortInfo, f = s.field;
15544             var st = this.fields.get(f).sortType;
15545             var fn = function(r1, r2){
15546                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15547                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15548             };
15549             this.data.sort(s.direction, fn);
15550             if(this.snapshot && this.snapshot != this.data){
15551                 this.snapshot.sort(s.direction, fn);
15552             }
15553         }
15554     },
15555
15556     /**
15557      * Sets the default sort column and order to be used by the next load operation.
15558      * @param {String} fieldName The name of the field to sort by.
15559      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15560      */
15561     setDefaultSort : function(field, dir){
15562         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15563     },
15564
15565     /**
15566      * Sort the Records.
15567      * If remote sorting is used, the sort is performed on the server, and the cache is
15568      * reloaded. If local sorting is used, the cache is sorted internally.
15569      * @param {String} fieldName The name of the field to sort by.
15570      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15571      */
15572     sort : function(fieldName, dir){
15573         var f = this.fields.get(fieldName);
15574         if(!dir){
15575             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15576             
15577             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15578                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15579             }else{
15580                 dir = f.sortDir;
15581             }
15582         }
15583         this.sortToggle[f.name] = dir;
15584         this.sortInfo = {field: f.name, direction: dir};
15585         if(!this.remoteSort){
15586             this.applySort();
15587             this.fireEvent("datachanged", this);
15588         }else{
15589             this.load(this.lastOptions);
15590         }
15591     },
15592
15593     /**
15594      * Calls the specified function for each of the Records in the cache.
15595      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15596      * Returning <em>false</em> aborts and exits the iteration.
15597      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15598      */
15599     each : function(fn, scope){
15600         this.data.each(fn, scope);
15601     },
15602
15603     /**
15604      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15605      * (e.g., during paging).
15606      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15607      */
15608     getModifiedRecords : function(){
15609         return this.modified;
15610     },
15611
15612     // private
15613     createFilterFn : function(property, value, anyMatch){
15614         if(!value.exec){ // not a regex
15615             value = String(value);
15616             if(value.length == 0){
15617                 return false;
15618             }
15619             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15620         }
15621         return function(r){
15622             return value.test(r.data[property]);
15623         };
15624     },
15625
15626     /**
15627      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15628      * @param {String} property A field on your records
15629      * @param {Number} start The record index to start at (defaults to 0)
15630      * @param {Number} end The last record index to include (defaults to length - 1)
15631      * @return {Number} The sum
15632      */
15633     sum : function(property, start, end){
15634         var rs = this.data.items, v = 0;
15635         start = start || 0;
15636         end = (end || end === 0) ? end : rs.length-1;
15637
15638         for(var i = start; i <= end; i++){
15639             v += (rs[i].data[property] || 0);
15640         }
15641         return v;
15642     },
15643
15644     /**
15645      * Filter the records by a specified property.
15646      * @param {String} field A field on your records
15647      * @param {String/RegExp} value Either a string that the field
15648      * should start with or a RegExp to test against the field
15649      * @param {Boolean} anyMatch True to match any part not just the beginning
15650      */
15651     filter : function(property, value, anyMatch){
15652         var fn = this.createFilterFn(property, value, anyMatch);
15653         return fn ? this.filterBy(fn) : this.clearFilter();
15654     },
15655
15656     /**
15657      * Filter by a function. The specified function will be called with each
15658      * record in this data source. If the function returns true the record is included,
15659      * otherwise it is filtered.
15660      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15661      * @param {Object} scope (optional) The scope of the function (defaults to this)
15662      */
15663     filterBy : function(fn, scope){
15664         this.snapshot = this.snapshot || this.data;
15665         this.data = this.queryBy(fn, scope||this);
15666         this.fireEvent("datachanged", this);
15667     },
15668
15669     /**
15670      * Query the records by a specified property.
15671      * @param {String} field A field on your records
15672      * @param {String/RegExp} value Either a string that the field
15673      * should start with or a RegExp to test against the field
15674      * @param {Boolean} anyMatch True to match any part not just the beginning
15675      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15676      */
15677     query : function(property, value, anyMatch){
15678         var fn = this.createFilterFn(property, value, anyMatch);
15679         return fn ? this.queryBy(fn) : this.data.clone();
15680     },
15681
15682     /**
15683      * Query by a function. The specified function will be called with each
15684      * record in this data source. If the function returns true the record is included
15685      * in the results.
15686      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15687      * @param {Object} scope (optional) The scope of the function (defaults to this)
15688       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15689      **/
15690     queryBy : function(fn, scope){
15691         var data = this.snapshot || this.data;
15692         return data.filterBy(fn, scope||this);
15693     },
15694
15695     /**
15696      * Collects unique values for a particular dataIndex from this store.
15697      * @param {String} dataIndex The property to collect
15698      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15699      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15700      * @return {Array} An array of the unique values
15701      **/
15702     collect : function(dataIndex, allowNull, bypassFilter){
15703         var d = (bypassFilter === true && this.snapshot) ?
15704                 this.snapshot.items : this.data.items;
15705         var v, sv, r = [], l = {};
15706         for(var i = 0, len = d.length; i < len; i++){
15707             v = d[i].data[dataIndex];
15708             sv = String(v);
15709             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15710                 l[sv] = true;
15711                 r[r.length] = v;
15712             }
15713         }
15714         return r;
15715     },
15716
15717     /**
15718      * Revert to a view of the Record cache with no filtering applied.
15719      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15720      */
15721     clearFilter : function(suppressEvent){
15722         if(this.snapshot && this.snapshot != this.data){
15723             this.data = this.snapshot;
15724             delete this.snapshot;
15725             if(suppressEvent !== true){
15726                 this.fireEvent("datachanged", this);
15727             }
15728         }
15729     },
15730
15731     // private
15732     afterEdit : function(record){
15733         if(this.modified.indexOf(record) == -1){
15734             this.modified.push(record);
15735         }
15736         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15737     },
15738     
15739     // private
15740     afterReject : function(record){
15741         this.modified.remove(record);
15742         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15743     },
15744
15745     // private
15746     afterCommit : function(record){
15747         this.modified.remove(record);
15748         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15749     },
15750
15751     /**
15752      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15753      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15754      */
15755     commitChanges : function(){
15756         var m = this.modified.slice(0);
15757         this.modified = [];
15758         for(var i = 0, len = m.length; i < len; i++){
15759             m[i].commit();
15760         }
15761     },
15762
15763     /**
15764      * Cancel outstanding changes on all changed records.
15765      */
15766     rejectChanges : function(){
15767         var m = this.modified.slice(0);
15768         this.modified = [];
15769         for(var i = 0, len = m.length; i < len; i++){
15770             m[i].reject();
15771         }
15772     },
15773
15774     onMetaChange : function(meta, rtype, o){
15775         this.recordType = rtype;
15776         this.fields = rtype.prototype.fields;
15777         delete this.snapshot;
15778         this.sortInfo = meta.sortInfo || this.sortInfo;
15779         this.modified = [];
15780         this.fireEvent('metachange', this, this.reader.meta);
15781     },
15782     
15783     moveIndex : function(data, type)
15784     {
15785         var index = this.indexOf(data);
15786         
15787         var newIndex = index + type;
15788         
15789         this.remove(data);
15790         
15791         this.insert(newIndex, data);
15792         
15793     }
15794 });/*
15795  * Based on:
15796  * Ext JS Library 1.1.1
15797  * Copyright(c) 2006-2007, Ext JS, LLC.
15798  *
15799  * Originally Released Under LGPL - original licence link has changed is not relivant.
15800  *
15801  * Fork - LGPL
15802  * <script type="text/javascript">
15803  */
15804
15805 /**
15806  * @class Roo.data.SimpleStore
15807  * @extends Roo.data.Store
15808  * Small helper class to make creating Stores from Array data easier.
15809  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15810  * @cfg {Array} fields An array of field definition objects, or field name strings.
15811  * @cfg {Object} an existing reader (eg. copied from another store)
15812  * @cfg {Array} data The multi-dimensional array of data
15813  * @cfg {Roo.data.DataProxy} proxy [not-required]  
15814  * @cfg {Roo.data.Reader} reader  [not-required] 
15815  * @constructor
15816  * @param {Object} config
15817  */
15818 Roo.data.SimpleStore = function(config)
15819 {
15820     Roo.data.SimpleStore.superclass.constructor.call(this, {
15821         isLocal : true,
15822         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15823                 id: config.id
15824             },
15825             Roo.data.Record.create(config.fields)
15826         ),
15827         proxy : new Roo.data.MemoryProxy(config.data)
15828     });
15829     this.load();
15830 };
15831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15832  * Based on:
15833  * Ext JS Library 1.1.1
15834  * Copyright(c) 2006-2007, Ext JS, LLC.
15835  *
15836  * Originally Released Under LGPL - original licence link has changed is not relivant.
15837  *
15838  * Fork - LGPL
15839  * <script type="text/javascript">
15840  */
15841
15842 /**
15843 /**
15844  * @extends Roo.data.Store
15845  * @class Roo.data.JsonStore
15846  * Small helper class to make creating Stores for JSON data easier. <br/>
15847 <pre><code>
15848 var store = new Roo.data.JsonStore({
15849     url: 'get-images.php',
15850     root: 'images',
15851     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15852 });
15853 </code></pre>
15854  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15855  * JsonReader and HttpProxy (unless inline data is provided).</b>
15856  * @cfg {Array} fields An array of field definition objects, or field name strings.
15857  * @constructor
15858  * @param {Object} config
15859  */
15860 Roo.data.JsonStore = function(c){
15861     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15862         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15863         reader: new Roo.data.JsonReader(c, c.fields)
15864     }));
15865 };
15866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15867  * Based on:
15868  * Ext JS Library 1.1.1
15869  * Copyright(c) 2006-2007, Ext JS, LLC.
15870  *
15871  * Originally Released Under LGPL - original licence link has changed is not relivant.
15872  *
15873  * Fork - LGPL
15874  * <script type="text/javascript">
15875  */
15876
15877  
15878 Roo.data.Field = function(config){
15879     if(typeof config == "string"){
15880         config = {name: config};
15881     }
15882     Roo.apply(this, config);
15883     
15884     if(!this.type){
15885         this.type = "auto";
15886     }
15887     
15888     var st = Roo.data.SortTypes;
15889     // named sortTypes are supported, here we look them up
15890     if(typeof this.sortType == "string"){
15891         this.sortType = st[this.sortType];
15892     }
15893     
15894     // set default sortType for strings and dates
15895     if(!this.sortType){
15896         switch(this.type){
15897             case "string":
15898                 this.sortType = st.asUCString;
15899                 break;
15900             case "date":
15901                 this.sortType = st.asDate;
15902                 break;
15903             default:
15904                 this.sortType = st.none;
15905         }
15906     }
15907
15908     // define once
15909     var stripRe = /[\$,%]/g;
15910
15911     // prebuilt conversion function for this field, instead of
15912     // switching every time we're reading a value
15913     if(!this.convert){
15914         var cv, dateFormat = this.dateFormat;
15915         switch(this.type){
15916             case "":
15917             case "auto":
15918             case undefined:
15919                 cv = function(v){ return v; };
15920                 break;
15921             case "string":
15922                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15923                 break;
15924             case "int":
15925                 cv = function(v){
15926                     return v !== undefined && v !== null && v !== '' ?
15927                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15928                     };
15929                 break;
15930             case "float":
15931                 cv = function(v){
15932                     return v !== undefined && v !== null && v !== '' ?
15933                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15934                     };
15935                 break;
15936             case "bool":
15937             case "boolean":
15938                 cv = function(v){ return v === true || v === "true" || v == 1; };
15939                 break;
15940             case "date":
15941                 cv = function(v){
15942                     if(!v){
15943                         return '';
15944                     }
15945                     if(v instanceof Date){
15946                         return v;
15947                     }
15948                     if(dateFormat){
15949                         if(dateFormat == "timestamp"){
15950                             return new Date(v*1000);
15951                         }
15952                         return Date.parseDate(v, dateFormat);
15953                     }
15954                     var parsed = Date.parse(v);
15955                     return parsed ? new Date(parsed) : null;
15956                 };
15957              break;
15958             
15959         }
15960         this.convert = cv;
15961     }
15962 };
15963
15964 Roo.data.Field.prototype = {
15965     dateFormat: null,
15966     defaultValue: "",
15967     mapping: null,
15968     sortType : null,
15969     sortDir : "ASC"
15970 };/*
15971  * Based on:
15972  * Ext JS Library 1.1.1
15973  * Copyright(c) 2006-2007, Ext JS, LLC.
15974  *
15975  * Originally Released Under LGPL - original licence link has changed is not relivant.
15976  *
15977  * Fork - LGPL
15978  * <script type="text/javascript">
15979  */
15980  
15981 // Base class for reading structured data from a data source.  This class is intended to be
15982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15983
15984 /**
15985  * @class Roo.data.DataReader
15986  * @abstract
15987  * Base class for reading structured data from a data source.  This class is intended to be
15988  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15989  */
15990
15991 Roo.data.DataReader = function(meta, recordType){
15992     
15993     this.meta = meta;
15994     
15995     this.recordType = recordType instanceof Array ? 
15996         Roo.data.Record.create(recordType) : recordType;
15997 };
15998
15999 Roo.data.DataReader.prototype = {
16000     
16001     
16002     readerType : 'Data',
16003      /**
16004      * Create an empty record
16005      * @param {Object} data (optional) - overlay some values
16006      * @return {Roo.data.Record} record created.
16007      */
16008     newRow :  function(d) {
16009         var da =  {};
16010         this.recordType.prototype.fields.each(function(c) {
16011             switch( c.type) {
16012                 case 'int' : da[c.name] = 0; break;
16013                 case 'date' : da[c.name] = new Date(); break;
16014                 case 'float' : da[c.name] = 0.0; break;
16015                 case 'boolean' : da[c.name] = false; break;
16016                 default : da[c.name] = ""; break;
16017             }
16018             
16019         });
16020         return new this.recordType(Roo.apply(da, d));
16021     }
16022     
16023     
16024 };/*
16025  * Based on:
16026  * Ext JS Library 1.1.1
16027  * Copyright(c) 2006-2007, Ext JS, LLC.
16028  *
16029  * Originally Released Under LGPL - original licence link has changed is not relivant.
16030  *
16031  * Fork - LGPL
16032  * <script type="text/javascript">
16033  */
16034
16035 /**
16036  * @class Roo.data.DataProxy
16037  * @extends Roo.util.Observable
16038  * @abstract
16039  * This class is an abstract base class for implementations which provide retrieval of
16040  * unformatted data objects.<br>
16041  * <p>
16042  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16043  * (of the appropriate type which knows how to parse the data object) to provide a block of
16044  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16045  * <p>
16046  * Custom implementations must implement the load method as described in
16047  * {@link Roo.data.HttpProxy#load}.
16048  */
16049 Roo.data.DataProxy = function(){
16050     this.addEvents({
16051         /**
16052          * @event beforeload
16053          * Fires before a network request is made to retrieve a data object.
16054          * @param {Object} This DataProxy object.
16055          * @param {Object} params The params parameter to the load function.
16056          */
16057         beforeload : true,
16058         /**
16059          * @event load
16060          * Fires before the load method's callback is called.
16061          * @param {Object} This DataProxy object.
16062          * @param {Object} o The data object.
16063          * @param {Object} arg The callback argument object passed to the load function.
16064          */
16065         load : true,
16066         /**
16067          * @event loadexception
16068          * Fires if an Exception occurs during data retrieval.
16069          * @param {Object} This DataProxy object.
16070          * @param {Object} o The data object.
16071          * @param {Object} arg The callback argument object passed to the load function.
16072          * @param {Object} e The Exception.
16073          */
16074         loadexception : true
16075     });
16076     Roo.data.DataProxy.superclass.constructor.call(this);
16077 };
16078
16079 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16080
16081     /**
16082      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16083      */
16084 /*
16085  * Based on:
16086  * Ext JS Library 1.1.1
16087  * Copyright(c) 2006-2007, Ext JS, LLC.
16088  *
16089  * Originally Released Under LGPL - original licence link has changed is not relivant.
16090  *
16091  * Fork - LGPL
16092  * <script type="text/javascript">
16093  */
16094 /**
16095  * @class Roo.data.MemoryProxy
16096  * @extends Roo.data.DataProxy
16097  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16098  * to the Reader when its load method is called.
16099  * @constructor
16100  * @param {Object} config  A config object containing the objects needed for the Store to access data,
16101  */
16102 Roo.data.MemoryProxy = function(config){
16103     var data = config;
16104     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16105         data = config.data;
16106     }
16107     Roo.data.MemoryProxy.superclass.constructor.call(this);
16108     this.data = data;
16109 };
16110
16111 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16112     
16113     /**
16114      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16115      */
16116     /**
16117      * Load data from the requested source (in this case an in-memory
16118      * data object passed to the constructor), read the data object into
16119      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16120      * process that block using the passed callback.
16121      * @param {Object} params This parameter is not used by the MemoryProxy class.
16122      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16123      * object into a block of Roo.data.Records.
16124      * @param {Function} callback The function into which to pass the block of Roo.data.records.
16125      * The function must be passed <ul>
16126      * <li>The Record block object</li>
16127      * <li>The "arg" argument from the load function</li>
16128      * <li>A boolean success indicator</li>
16129      * </ul>
16130      * @param {Object} scope The scope in which to call the callback
16131      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16132      */
16133     load : function(params, reader, callback, scope, arg){
16134         params = params || {};
16135         var result;
16136         try {
16137             result = reader.readRecords(params.data ? params.data :this.data);
16138         }catch(e){
16139             this.fireEvent("loadexception", this, arg, null, e);
16140             callback.call(scope, null, arg, false);
16141             return;
16142         }
16143         callback.call(scope, result, arg, true);
16144     },
16145     
16146     // private
16147     update : function(params, records){
16148         
16149     }
16150 });/*
16151  * Based on:
16152  * Ext JS Library 1.1.1
16153  * Copyright(c) 2006-2007, Ext JS, LLC.
16154  *
16155  * Originally Released Under LGPL - original licence link has changed is not relivant.
16156  *
16157  * Fork - LGPL
16158  * <script type="text/javascript">
16159  */
16160 /**
16161  * @class Roo.data.HttpProxy
16162  * @extends Roo.data.DataProxy
16163  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16164  * configured to reference a certain URL.<br><br>
16165  * <p>
16166  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16167  * from which the running page was served.<br><br>
16168  * <p>
16169  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16170  * <p>
16171  * Be aware that to enable the browser to parse an XML document, the server must set
16172  * the Content-Type header in the HTTP response to "text/xml".
16173  * @constructor
16174  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16175  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
16176  * will be used to make the request.
16177  */
16178 Roo.data.HttpProxy = function(conn){
16179     Roo.data.HttpProxy.superclass.constructor.call(this);
16180     // is conn a conn config or a real conn?
16181     this.conn = conn;
16182     this.useAjax = !conn || !conn.events;
16183   
16184 };
16185
16186 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16187     // thse are take from connection...
16188     
16189     /**
16190      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16191      */
16192     /**
16193      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16194      * extra parameters to each request made by this object. (defaults to undefined)
16195      */
16196     /**
16197      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16198      *  to each request made by this object. (defaults to undefined)
16199      */
16200     /**
16201      * @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)
16202      */
16203     /**
16204      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16205      */
16206      /**
16207      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16208      * @type Boolean
16209      */
16210   
16211
16212     /**
16213      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16214      * @type Boolean
16215      */
16216     /**
16217      * Return the {@link Roo.data.Connection} object being used by this Proxy.
16218      * @return {Connection} The Connection object. This object may be used to subscribe to events on
16219      * a finer-grained basis than the DataProxy events.
16220      */
16221     getConnection : function(){
16222         return this.useAjax ? Roo.Ajax : this.conn;
16223     },
16224
16225     /**
16226      * Load data from the configured {@link Roo.data.Connection}, read the data object into
16227      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16228      * process that block using the passed callback.
16229      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16230      * for the request to the remote server.
16231      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16232      * object into a block of Roo.data.Records.
16233      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16234      * The function must be passed <ul>
16235      * <li>The Record block object</li>
16236      * <li>The "arg" argument from the load function</li>
16237      * <li>A boolean success indicator</li>
16238      * </ul>
16239      * @param {Object} scope The scope in which to call the callback
16240      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16241      */
16242     load : function(params, reader, callback, scope, arg){
16243         if(this.fireEvent("beforeload", this, params) !== false){
16244             var  o = {
16245                 params : params || {},
16246                 request: {
16247                     callback : callback,
16248                     scope : scope,
16249                     arg : arg
16250                 },
16251                 reader: reader,
16252                 callback : this.loadResponse,
16253                 scope: this
16254             };
16255             if(this.useAjax){
16256                 Roo.applyIf(o, this.conn);
16257                 if(this.activeRequest){
16258                     Roo.Ajax.abort(this.activeRequest);
16259                 }
16260                 this.activeRequest = Roo.Ajax.request(o);
16261             }else{
16262                 this.conn.request(o);
16263             }
16264         }else{
16265             callback.call(scope||this, null, arg, false);
16266         }
16267     },
16268
16269     // private
16270     loadResponse : function(o, success, response){
16271         delete this.activeRequest;
16272         if(!success){
16273             this.fireEvent("loadexception", this, o, response);
16274             o.request.callback.call(o.request.scope, null, o.request.arg, false);
16275             return;
16276         }
16277         var result;
16278         try {
16279             result = o.reader.read(response);
16280         }catch(e){
16281             o.success = false;
16282             o.raw = { errorMsg : response.responseText };
16283             this.fireEvent("loadexception", this, o, response, e);
16284             o.request.callback.call(o.request.scope, o, o.request.arg, false);
16285             return;
16286         }
16287         
16288         this.fireEvent("load", this, o, o.request.arg);
16289         o.request.callback.call(o.request.scope, result, o.request.arg, true);
16290     },
16291
16292     // private
16293     update : function(dataSet){
16294
16295     },
16296
16297     // private
16298     updateResponse : function(dataSet){
16299
16300     }
16301 });/*
16302  * Based on:
16303  * Ext JS Library 1.1.1
16304  * Copyright(c) 2006-2007, Ext JS, LLC.
16305  *
16306  * Originally Released Under LGPL - original licence link has changed is not relivant.
16307  *
16308  * Fork - LGPL
16309  * <script type="text/javascript">
16310  */
16311
16312 /**
16313  * @class Roo.data.ScriptTagProxy
16314  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16315  * other than the originating domain of the running page.<br><br>
16316  * <p>
16317  * <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
16318  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16319  * <p>
16320  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16321  * source code that is used as the source inside a &lt;script> tag.<br><br>
16322  * <p>
16323  * In order for the browser to process the returned data, the server must wrap the data object
16324  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16325  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16326  * depending on whether the callback name was passed:
16327  * <p>
16328  * <pre><code>
16329 boolean scriptTag = false;
16330 String cb = request.getParameter("callback");
16331 if (cb != null) {
16332     scriptTag = true;
16333     response.setContentType("text/javascript");
16334 } else {
16335     response.setContentType("application/x-json");
16336 }
16337 Writer out = response.getWriter();
16338 if (scriptTag) {
16339     out.write(cb + "(");
16340 }
16341 out.print(dataBlock.toJsonString());
16342 if (scriptTag) {
16343     out.write(");");
16344 }
16345 </pre></code>
16346  *
16347  * @constructor
16348  * @param {Object} config A configuration object.
16349  */
16350 Roo.data.ScriptTagProxy = function(config){
16351     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16352     Roo.apply(this, config);
16353     this.head = document.getElementsByTagName("head")[0];
16354 };
16355
16356 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16357
16358 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16359     /**
16360      * @cfg {String} url The URL from which to request the data object.
16361      */
16362     /**
16363      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16364      */
16365     timeout : 30000,
16366     /**
16367      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16368      * the server the name of the callback function set up by the load call to process the returned data object.
16369      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16370      * javascript output which calls this named function passing the data object as its only parameter.
16371      */
16372     callbackParam : "callback",
16373     /**
16374      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16375      * name to the request.
16376      */
16377     nocache : true,
16378
16379     /**
16380      * Load data from the configured URL, read the data object into
16381      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16382      * process that block using the passed callback.
16383      * @param {Object} params An object containing properties which are to be used as HTTP parameters
16384      * for the request to the remote server.
16385      * @param {Roo.data.DataReader} reader The Reader object which converts the data
16386      * object into a block of Roo.data.Records.
16387      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16388      * The function must be passed <ul>
16389      * <li>The Record block object</li>
16390      * <li>The "arg" argument from the load function</li>
16391      * <li>A boolean success indicator</li>
16392      * </ul>
16393      * @param {Object} scope The scope in which to call the callback
16394      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16395      */
16396     load : function(params, reader, callback, scope, arg){
16397         if(this.fireEvent("beforeload", this, params) !== false){
16398
16399             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16400
16401             var url = this.url;
16402             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16403             if(this.nocache){
16404                 url += "&_dc=" + (new Date().getTime());
16405             }
16406             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16407             var trans = {
16408                 id : transId,
16409                 cb : "stcCallback"+transId,
16410                 scriptId : "stcScript"+transId,
16411                 params : params,
16412                 arg : arg,
16413                 url : url,
16414                 callback : callback,
16415                 scope : scope,
16416                 reader : reader
16417             };
16418             var conn = this;
16419
16420             window[trans.cb] = function(o){
16421                 conn.handleResponse(o, trans);
16422             };
16423
16424             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16425
16426             if(this.autoAbort !== false){
16427                 this.abort();
16428             }
16429
16430             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16431
16432             var script = document.createElement("script");
16433             script.setAttribute("src", url);
16434             script.setAttribute("type", "text/javascript");
16435             script.setAttribute("id", trans.scriptId);
16436             this.head.appendChild(script);
16437
16438             this.trans = trans;
16439         }else{
16440             callback.call(scope||this, null, arg, false);
16441         }
16442     },
16443
16444     // private
16445     isLoading : function(){
16446         return this.trans ? true : false;
16447     },
16448
16449     /**
16450      * Abort the current server request.
16451      */
16452     abort : function(){
16453         if(this.isLoading()){
16454             this.destroyTrans(this.trans);
16455         }
16456     },
16457
16458     // private
16459     destroyTrans : function(trans, isLoaded){
16460         this.head.removeChild(document.getElementById(trans.scriptId));
16461         clearTimeout(trans.timeoutId);
16462         if(isLoaded){
16463             window[trans.cb] = undefined;
16464             try{
16465                 delete window[trans.cb];
16466             }catch(e){}
16467         }else{
16468             // if hasn't been loaded, wait for load to remove it to prevent script error
16469             window[trans.cb] = function(){
16470                 window[trans.cb] = undefined;
16471                 try{
16472                     delete window[trans.cb];
16473                 }catch(e){}
16474             };
16475         }
16476     },
16477
16478     // private
16479     handleResponse : function(o, trans){
16480         this.trans = false;
16481         this.destroyTrans(trans, true);
16482         var result;
16483         try {
16484             result = trans.reader.readRecords(o);
16485         }catch(e){
16486             this.fireEvent("loadexception", this, o, trans.arg, e);
16487             trans.callback.call(trans.scope||window, null, trans.arg, false);
16488             return;
16489         }
16490         this.fireEvent("load", this, o, trans.arg);
16491         trans.callback.call(trans.scope||window, result, trans.arg, true);
16492     },
16493
16494     // private
16495     handleFailure : function(trans){
16496         this.trans = false;
16497         this.destroyTrans(trans, false);
16498         this.fireEvent("loadexception", this, null, trans.arg);
16499         trans.callback.call(trans.scope||window, null, trans.arg, false);
16500     }
16501 });/*
16502  * Based on:
16503  * Ext JS Library 1.1.1
16504  * Copyright(c) 2006-2007, Ext JS, LLC.
16505  *
16506  * Originally Released Under LGPL - original licence link has changed is not relivant.
16507  *
16508  * Fork - LGPL
16509  * <script type="text/javascript">
16510  */
16511
16512 /**
16513  * @class Roo.data.JsonReader
16514  * @extends Roo.data.DataReader
16515  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16516  * based on mappings in a provided Roo.data.Record constructor.
16517  * 
16518  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16519  * in the reply previously. 
16520  * 
16521  * <p>
16522  * Example code:
16523  * <pre><code>
16524 var RecordDef = Roo.data.Record.create([
16525     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16526     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16527 ]);
16528 var myReader = new Roo.data.JsonReader({
16529     totalProperty: "results",    // The property which contains the total dataset size (optional)
16530     root: "rows",                // The property which contains an Array of row objects
16531     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16532 }, RecordDef);
16533 </code></pre>
16534  * <p>
16535  * This would consume a JSON file like this:
16536  * <pre><code>
16537 { 'results': 2, 'rows': [
16538     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16539     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16540 }
16541 </code></pre>
16542  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16543  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16544  * paged from the remote server.
16545  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16546  * @cfg {String} root name of the property which contains the Array of row objects.
16547  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16548  * @cfg {Array} fields Array of field definition objects
16549  * @constructor
16550  * Create a new JsonReader
16551  * @param {Object} meta Metadata configuration options
16552  * @param {Object} recordType Either an Array of field definition objects,
16553  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16554  */
16555 Roo.data.JsonReader = function(meta, recordType){
16556     
16557     meta = meta || {};
16558     // set some defaults:
16559     Roo.applyIf(meta, {
16560         totalProperty: 'total',
16561         successProperty : 'success',
16562         root : 'data',
16563         id : 'id'
16564     });
16565     
16566     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16567 };
16568 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16569     
16570     readerType : 'Json',
16571     
16572     /**
16573      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16574      * Used by Store query builder to append _requestMeta to params.
16575      * 
16576      */
16577     metaFromRemote : false,
16578     /**
16579      * This method is only used by a DataProxy which has retrieved data from a remote server.
16580      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16581      * @return {Object} data A data block which is used by an Roo.data.Store object as
16582      * a cache of Roo.data.Records.
16583      */
16584     read : function(response){
16585         var json = response.responseText;
16586        
16587         var o = /* eval:var:o */ eval("("+json+")");
16588         if(!o) {
16589             throw {message: "JsonReader.read: Json object not found"};
16590         }
16591         
16592         if(o.metaData){
16593             
16594             delete this.ef;
16595             this.metaFromRemote = true;
16596             this.meta = o.metaData;
16597             this.recordType = Roo.data.Record.create(o.metaData.fields);
16598             this.onMetaChange(this.meta, this.recordType, o);
16599         }
16600         return this.readRecords(o);
16601     },
16602
16603     // private function a store will implement
16604     onMetaChange : function(meta, recordType, o){
16605
16606     },
16607
16608     /**
16609          * @ignore
16610          */
16611     simpleAccess: function(obj, subsc) {
16612         return obj[subsc];
16613     },
16614
16615         /**
16616          * @ignore
16617          */
16618     getJsonAccessor: function(){
16619         var re = /[\[\.]/;
16620         return function(expr) {
16621             try {
16622                 return(re.test(expr))
16623                     ? new Function("obj", "return obj." + expr)
16624                     : function(obj){
16625                         return obj[expr];
16626                     };
16627             } catch(e){}
16628             return Roo.emptyFn;
16629         };
16630     }(),
16631
16632     /**
16633      * Create a data block containing Roo.data.Records from an XML document.
16634      * @param {Object} o An object which contains an Array of row objects in the property specified
16635      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16636      * which contains the total size of the dataset.
16637      * @return {Object} data A data block which is used by an Roo.data.Store object as
16638      * a cache of Roo.data.Records.
16639      */
16640     readRecords : function(o){
16641         /**
16642          * After any data loads, the raw JSON data is available for further custom processing.
16643          * @type Object
16644          */
16645         this.o = o;
16646         var s = this.meta, Record = this.recordType,
16647             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16648
16649 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16650         if (!this.ef) {
16651             if(s.totalProperty) {
16652                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16653                 }
16654                 if(s.successProperty) {
16655                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16656                 }
16657                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16658                 if (s.id) {
16659                         var g = this.getJsonAccessor(s.id);
16660                         this.getId = function(rec) {
16661                                 var r = g(rec);  
16662                                 return (r === undefined || r === "") ? null : r;
16663                         };
16664                 } else {
16665                         this.getId = function(){return null;};
16666                 }
16667             this.ef = [];
16668             for(var jj = 0; jj < fl; jj++){
16669                 f = fi[jj];
16670                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16671                 this.ef[jj] = this.getJsonAccessor(map);
16672             }
16673         }
16674
16675         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16676         if(s.totalProperty){
16677             var vt = parseInt(this.getTotal(o), 10);
16678             if(!isNaN(vt)){
16679                 totalRecords = vt;
16680             }
16681         }
16682         if(s.successProperty){
16683             var vs = this.getSuccess(o);
16684             if(vs === false || vs === 'false'){
16685                 success = false;
16686             }
16687         }
16688         var records = [];
16689         for(var i = 0; i < c; i++){
16690             var n = root[i];
16691             var values = {};
16692             var id = this.getId(n);
16693             for(var j = 0; j < fl; j++){
16694                 f = fi[j];
16695                                 var v = this.ef[j](n);
16696                                 if (!f.convert) {
16697                                         Roo.log('missing convert for ' + f.name);
16698                                         Roo.log(f);
16699                                         continue;
16700                                 }
16701                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16702             }
16703                         if (!Record) {
16704                                 return {
16705                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16706                                         success : false,
16707                                         records : [],
16708                                         totalRecords : 0
16709                                 };
16710                         }
16711             var record = new Record(values, id);
16712             record.json = n;
16713             records[i] = record;
16714         }
16715         return {
16716             raw : o,
16717             success : success,
16718             records : records,
16719             totalRecords : totalRecords
16720         };
16721     },
16722     // used when loading children.. @see loadDataFromChildren
16723     toLoadData: function(rec)
16724     {
16725         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16726         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16727         return { data : data, total : data.length };
16728         
16729     }
16730 });/*
16731  * Based on:
16732  * Ext JS Library 1.1.1
16733  * Copyright(c) 2006-2007, Ext JS, LLC.
16734  *
16735  * Originally Released Under LGPL - original licence link has changed is not relivant.
16736  *
16737  * Fork - LGPL
16738  * <script type="text/javascript">
16739  */
16740
16741 /**
16742  * @class Roo.data.ArrayReader
16743  * @extends Roo.data.DataReader
16744  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16745  * Each element of that Array represents a row of data fields. The
16746  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16747  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16748  * <p>
16749  * Example code:.
16750  * <pre><code>
16751 var RecordDef = Roo.data.Record.create([
16752     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16753     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16754 ]);
16755 var myReader = new Roo.data.ArrayReader({
16756     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16757 }, RecordDef);
16758 </code></pre>
16759  * <p>
16760  * This would consume an Array like this:
16761  * <pre><code>
16762 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16763   </code></pre>
16764  
16765  * @constructor
16766  * Create a new JsonReader
16767  * @param {Object} meta Metadata configuration options.
16768  * @param {Object|Array} recordType Either an Array of field definition objects
16769  * 
16770  * @cfg {Array} fields Array of field definition objects
16771  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16772  * as specified to {@link Roo.data.Record#create},
16773  * or an {@link Roo.data.Record} object
16774  *
16775  * 
16776  * created using {@link Roo.data.Record#create}.
16777  */
16778 Roo.data.ArrayReader = function(meta, recordType)
16779 {    
16780     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16781 };
16782
16783 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16784     
16785       /**
16786      * Create a data block containing Roo.data.Records from an XML document.
16787      * @param {Object} o An Array of row objects which represents the dataset.
16788      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16789      * a cache of Roo.data.Records.
16790      */
16791     readRecords : function(o)
16792     {
16793         var sid = this.meta ? this.meta.id : null;
16794         var recordType = this.recordType, fields = recordType.prototype.fields;
16795         var records = [];
16796         var root = o;
16797         for(var i = 0; i < root.length; i++){
16798             var n = root[i];
16799             var values = {};
16800             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16801             for(var j = 0, jlen = fields.length; j < jlen; j++){
16802                 var f = fields.items[j];
16803                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16804                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16805                 v = f.convert(v);
16806                 values[f.name] = v;
16807             }
16808             var record = new recordType(values, id);
16809             record.json = n;
16810             records[records.length] = record;
16811         }
16812         return {
16813             records : records,
16814             totalRecords : records.length
16815         };
16816     },
16817     // used when loading children.. @see loadDataFromChildren
16818     toLoadData: function(rec)
16819     {
16820         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16821         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16822         
16823     }
16824     
16825     
16826 });/*
16827  * - LGPL
16828  * * 
16829  */
16830
16831 /**
16832  * @class Roo.bootstrap.form.ComboBox
16833  * @extends Roo.bootstrap.form.TriggerField
16834  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16835  * @cfg {Boolean} append (true|false) default false
16836  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16837  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16838  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16839  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16840  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16841  * @cfg {Boolean} animate default true
16842  * @cfg {Boolean} emptyResultText only for touch device
16843  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16844  * @cfg {String} emptyTitle default ''
16845  * @cfg {Number} width fixed with? experimental
16846  * @constructor
16847  * Create a new ComboBox.
16848  * @param {Object} config Configuration options
16849  */
16850 Roo.bootstrap.form.ComboBox = function(config){
16851     Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16852     this.addEvents({
16853         /**
16854          * @event expand
16855          * Fires when the dropdown list is expanded
16856         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857         */
16858         'expand' : true,
16859         /**
16860          * @event collapse
16861          * Fires when the dropdown list is collapsed
16862         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863         */
16864         'collapse' : true,
16865         /**
16866          * @event beforeselect
16867          * Fires before a list item is selected. Return false to cancel the selection.
16868         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869         * @param {Roo.data.Record} record The data record returned from the underlying store
16870         * @param {Number} index The index of the selected item in the dropdown list
16871         */
16872         'beforeselect' : true,
16873         /**
16874          * @event select
16875          * Fires when a list item is selected
16876         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16878         * @param {Number} index The index of the selected item in the dropdown list
16879         */
16880         'select' : true,
16881         /**
16882          * @event beforequery
16883          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16884          * The event object passed has these properties:
16885         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16886         * @param {String} query The query
16887         * @param {Boolean} forceAll true to force "all" query
16888         * @param {Boolean} cancel true to cancel the query
16889         * @param {Object} e The query event object
16890         */
16891         'beforequery': true,
16892          /**
16893          * @event add
16894          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16895         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896         */
16897         'add' : true,
16898         /**
16899          * @event edit
16900          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16901         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16903         */
16904         'edit' : true,
16905         /**
16906          * @event remove
16907          * Fires when the remove value from the combobox array
16908         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909         */
16910         'remove' : true,
16911         /**
16912          * @event afterremove
16913          * Fires when the remove value from the combobox array
16914         * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915         */
16916         'afterremove' : true,
16917         /**
16918          * @event specialfilter
16919          * Fires when specialfilter
16920             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921             */
16922         'specialfilter' : true,
16923         /**
16924          * @event tick
16925          * Fires when tick the element
16926             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927             */
16928         'tick' : true,
16929         /**
16930          * @event touchviewdisplay
16931          * Fires when touch view require special display (default is using displayField)
16932             * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933             * @param {Object} cfg set html .
16934             */
16935         'touchviewdisplay' : true
16936         
16937     });
16938     
16939     this.item = [];
16940     this.tickItems = [];
16941     
16942     this.selectedIndex = -1;
16943     if(this.mode == 'local'){
16944         if(config.queryDelay === undefined){
16945             this.queryDelay = 10;
16946         }
16947         if(config.minChars === undefined){
16948             this.minChars = 0;
16949         }
16950     }
16951 };
16952
16953 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16954      
16955     /**
16956      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16957      * rendering into an Roo.Editor, defaults to false)
16958      */
16959     /**
16960      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16961      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16962      */
16963     /**
16964      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16965      */
16966     /**
16967      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16968      * the dropdown list (defaults to undefined, with no header element)
16969      */
16970
16971      /**
16972      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16973      */
16974      
16975      /**
16976      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16977      */
16978     listWidth: undefined,
16979     /**
16980      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16981      * mode = 'remote' or 'text' if mode = 'local')
16982      */
16983     displayField: undefined,
16984     
16985     /**
16986      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16987      * mode = 'remote' or 'value' if mode = 'local'). 
16988      * Note: use of a valueField requires the user make a selection
16989      * in order for a value to be mapped.
16990      */
16991     valueField: undefined,
16992     /**
16993      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16994      */
16995     modalTitle : '',
16996     
16997     /**
16998      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16999      * field's data value (defaults to the underlying DOM element's name)
17000      */
17001     hiddenName: undefined,
17002     /**
17003      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17004      */
17005     listClass: '',
17006     /**
17007      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17008      */
17009     selectedClass: 'active',
17010     
17011     /**
17012      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17013      */
17014     shadow:'sides',
17015     /**
17016      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17017      * anchor positions (defaults to 'tl-bl')
17018      */
17019     listAlign: 'tl-bl?',
17020     /**
17021      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17022      */
17023     maxHeight: 300,
17024     /**
17025      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
17026      * query specified by the allQuery config option (defaults to 'query')
17027      */
17028     triggerAction: 'query',
17029     /**
17030      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17031      * (defaults to 4, does not apply if editable = false)
17032      */
17033     minChars : 4,
17034     /**
17035      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17036      * delay (typeAheadDelay) if it matches a known value (defaults to false)
17037      */
17038     typeAhead: false,
17039     /**
17040      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17041      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17042      */
17043     queryDelay: 500,
17044     /**
17045      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17046      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
17047      */
17048     pageSize: 0,
17049     /**
17050      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
17051      * when editable = true (defaults to false)
17052      */
17053     selectOnFocus:false,
17054     /**
17055      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17056      */
17057     queryParam: 'query',
17058     /**
17059      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
17060      * when mode = 'remote' (defaults to 'Loading...')
17061      */
17062     loadingText: 'Loading...',
17063     /**
17064      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17065      */
17066     resizable: false,
17067     /**
17068      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17069      */
17070     handleHeight : 8,
17071     /**
17072      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17073      * traditional select (defaults to true)
17074      */
17075     editable: true,
17076     /**
17077      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17078      */
17079     allQuery: '',
17080     /**
17081      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17082      */
17083     mode: 'remote',
17084     /**
17085      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17086      * listWidth has a higher value)
17087      */
17088     minListWidth : 70,
17089     /**
17090      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17091      * allow the user to set arbitrary text into the field (defaults to false)
17092      */
17093     forceSelection:false,
17094     /**
17095      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17096      * if typeAhead = true (defaults to 250)
17097      */
17098     typeAheadDelay : 250,
17099     /**
17100      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17101      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17102      */
17103     valueNotFoundText : undefined,
17104     /**
17105      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17106      */
17107     blockFocus : false,
17108     
17109     /**
17110      * @cfg {Boolean} disableClear Disable showing of clear button.
17111      */
17112     disableClear : false,
17113     /**
17114      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
17115      */
17116     alwaysQuery : false,
17117     
17118     /**
17119      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
17120      */
17121     multiple : false,
17122     
17123     /**
17124      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17125      */
17126     invalidClass : "has-warning",
17127     
17128     /**
17129      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17130      */
17131     validClass : "has-success",
17132     
17133     /**
17134      * @cfg {Boolean} specialFilter (true|false) special filter default false
17135      */
17136     specialFilter : false,
17137     
17138     /**
17139      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17140      */
17141     mobileTouchView : true,
17142     
17143     /**
17144      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17145      */
17146     useNativeIOS : false,
17147     
17148     /**
17149      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17150      */
17151     mobile_restrict_height : false,
17152     
17153     ios_options : false,
17154     
17155     //private
17156     addicon : false,
17157     editicon: false,
17158     
17159     page: 0,
17160     hasQuery: false,
17161     append: false,
17162     loadNext: false,
17163     autoFocus : true,
17164     tickable : false,
17165     btnPosition : 'right',
17166     triggerList : true,
17167     showToggleBtn : true,
17168     animate : true,
17169     emptyResultText: 'Empty',
17170     triggerText : 'Select',
17171     emptyTitle : '',
17172     width : false,
17173     
17174     // element that contains real text value.. (when hidden is used..)
17175     
17176     getAutoCreate : function()
17177     {   
17178         var cfg = false;
17179         //render
17180         /*
17181          * Render classic select for iso
17182          */
17183         
17184         if(Roo.isIOS && this.useNativeIOS){
17185             cfg = this.getAutoCreateNativeIOS();
17186             return cfg;
17187         }
17188         
17189         /*
17190          * Touch Devices
17191          */
17192         
17193         if(Roo.isTouch && this.mobileTouchView){
17194             cfg = this.getAutoCreateTouchView();
17195             return cfg;;
17196         }
17197         
17198         /*
17199          *  Normal ComboBox
17200          */
17201         if(!this.tickable){
17202             cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17203             return cfg;
17204         }
17205         
17206         /*
17207          *  ComboBox with tickable selections
17208          */
17209              
17210         var align = this.labelAlign || this.parentLabelAlign();
17211         
17212         cfg = {
17213             cls : 'form-group roo-combobox-tickable' //input-group
17214         };
17215         
17216         var btn_text_select = '';
17217         var btn_text_done = '';
17218         var btn_text_cancel = '';
17219         
17220         if (this.btn_text_show) {
17221             btn_text_select = 'Select';
17222             btn_text_done = 'Done';
17223             btn_text_cancel = 'Cancel'; 
17224         }
17225         
17226         var buttons = {
17227             tag : 'div',
17228             cls : 'tickable-buttons',
17229             cn : [
17230                 {
17231                     tag : 'button',
17232                     type : 'button',
17233                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17234                     //html : this.triggerText
17235                     html: btn_text_select
17236                 },
17237                 {
17238                     tag : 'button',
17239                     type : 'button',
17240                     name : 'ok',
17241                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17242                     //html : 'Done'
17243                     html: btn_text_done
17244                 },
17245                 {
17246                     tag : 'button',
17247                     type : 'button',
17248                     name : 'cancel',
17249                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17250                     //html : 'Cancel'
17251                     html: btn_text_cancel
17252                 }
17253             ]
17254         };
17255         
17256         if(this.editable){
17257             buttons.cn.unshift({
17258                 tag: 'input',
17259                 cls: 'roo-select2-search-field-input'
17260             });
17261         }
17262         
17263         var _this = this;
17264         
17265         Roo.each(buttons.cn, function(c){
17266             if (_this.size) {
17267                 c.cls += ' btn-' + _this.size;
17268             }
17269
17270             if (_this.disabled) {
17271                 c.disabled = true;
17272             }
17273         });
17274         
17275         var box = {
17276             tag: 'div',
17277             style : 'display: contents',
17278             cn: [
17279                 {
17280                     tag: 'input',
17281                     type : 'hidden',
17282                     cls: 'form-hidden-field'
17283                 },
17284                 {
17285                     tag: 'ul',
17286                     cls: 'roo-select2-choices',
17287                     cn:[
17288                         {
17289                             tag: 'li',
17290                             cls: 'roo-select2-search-field',
17291                             cn: [
17292                                 buttons
17293                             ]
17294                         }
17295                     ]
17296                 }
17297             ]
17298         };
17299         
17300         var combobox = {
17301             cls: 'roo-select2-container input-group roo-select2-container-multi',
17302             cn: [
17303                 
17304                 box
17305 //                {
17306 //                    tag: 'ul',
17307 //                    cls: 'typeahead typeahead-long dropdown-menu',
17308 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
17309 //                }
17310             ]
17311         };
17312         
17313         if(this.hasFeedback && !this.allowBlank){
17314             
17315             var feedback = {
17316                 tag: 'span',
17317                 cls: 'glyphicon form-control-feedback'
17318             };
17319
17320             combobox.cn.push(feedback);
17321         }
17322         
17323         
17324         
17325         var indicator = {
17326             tag : 'i',
17327             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17328             tooltip : 'This field is required'
17329         };
17330         if (Roo.bootstrap.version == 4) {
17331             indicator = {
17332                 tag : 'i',
17333                 style : 'display:none'
17334             };
17335         }
17336         if (align ==='left' && this.fieldLabel.length) {
17337             
17338             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
17339             
17340             cfg.cn = [
17341                 indicator,
17342                 {
17343                     tag: 'label',
17344                     'for' :  id,
17345                     cls : 'control-label col-form-label',
17346                     html : this.fieldLabel
17347
17348                 },
17349                 {
17350                     cls : "", 
17351                     cn: [
17352                         combobox
17353                     ]
17354                 }
17355
17356             ];
17357             
17358             var labelCfg = cfg.cn[1];
17359             var contentCfg = cfg.cn[2];
17360             
17361
17362             if(this.indicatorpos == 'right'){
17363                 
17364                 cfg.cn = [
17365                     {
17366                         tag: 'label',
17367                         'for' :  id,
17368                         cls : 'control-label col-form-label',
17369                         cn : [
17370                             {
17371                                 tag : 'span',
17372                                 html : this.fieldLabel
17373                             },
17374                             indicator
17375                         ]
17376                     },
17377                     {
17378                         cls : "",
17379                         cn: [
17380                             combobox
17381                         ]
17382                     }
17383
17384                 ];
17385                 
17386                 
17387                 
17388                 labelCfg = cfg.cn[0];
17389                 contentCfg = cfg.cn[1];
17390             
17391             }
17392             
17393             if(this.labelWidth > 12){
17394                 labelCfg.style = "width: " + this.labelWidth + 'px';
17395             }
17396             if(this.width * 1 > 0){
17397                 contentCfg.style = "width: " + this.width + 'px';
17398             }
17399             if(this.labelWidth < 13 && this.labelmd == 0){
17400                 this.labelmd = this.labelWidth;
17401             }
17402             
17403             if(this.labellg > 0){
17404                 labelCfg.cls += ' col-lg-' + this.labellg;
17405                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17406             }
17407             
17408             if(this.labelmd > 0){
17409                 labelCfg.cls += ' col-md-' + this.labelmd;
17410                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17411             }
17412             
17413             if(this.labelsm > 0){
17414                 labelCfg.cls += ' col-sm-' + this.labelsm;
17415                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17416             }
17417             
17418             if(this.labelxs > 0){
17419                 labelCfg.cls += ' col-xs-' + this.labelxs;
17420                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17421             }
17422                 
17423                 
17424         } else if ( this.fieldLabel.length) {
17425 //                Roo.log(" label");
17426                  cfg.cn = [
17427                    indicator,
17428                     {
17429                         tag: 'label',
17430                         //cls : 'input-group-addon',
17431                         html : this.fieldLabel
17432                     },
17433                     combobox
17434                 ];
17435                 
17436                 if(this.indicatorpos == 'right'){
17437                     cfg.cn = [
17438                         {
17439                             tag: 'label',
17440                             //cls : 'input-group-addon',
17441                             html : this.fieldLabel
17442                         },
17443                         indicator,
17444                         combobox
17445                     ];
17446                     
17447                 }
17448
17449         } else {
17450             
17451 //                Roo.log(" no label && no align");
17452                 cfg = combobox
17453                      
17454                 
17455         }
17456          
17457         var settings=this;
17458         ['xs','sm','md','lg'].map(function(size){
17459             if (settings[size]) {
17460                 cfg.cls += ' col-' + size + '-' + settings[size];
17461             }
17462         });
17463         
17464         return cfg;
17465         
17466     },
17467     
17468     _initEventsCalled : false,
17469     
17470     // private
17471     initEvents: function()
17472     {   
17473         if (this._initEventsCalled) { // as we call render... prevent looping...
17474             return;
17475         }
17476         this._initEventsCalled = true;
17477         
17478         if (!this.store) {
17479             throw "can not find store for combo";
17480         }
17481         
17482         this.indicator = this.indicatorEl();
17483         
17484         this.store = Roo.factory(this.store, Roo.data);
17485         this.store.parent = this;
17486         
17487         // if we are building from html. then this element is so complex, that we can not really
17488         // use the rendered HTML.
17489         // so we have to trash and replace the previous code.
17490         if (Roo.XComponent.build_from_html) {
17491             // remove this element....
17492             var e = this.el.dom, k=0;
17493             while (e ) { e = e.previousSibling;  ++k;}
17494
17495             this.el.remove();
17496             
17497             this.el=false;
17498             this.rendered = false;
17499             
17500             this.render(this.parent().getChildContainer(true), k);
17501         }
17502         
17503         if(Roo.isIOS && this.useNativeIOS){
17504             this.initIOSView();
17505             return;
17506         }
17507         
17508         /*
17509          * Touch Devices
17510          */
17511         
17512         if(Roo.isTouch && this.mobileTouchView){
17513             this.initTouchView();
17514             return;
17515         }
17516         
17517         if(this.tickable){
17518             this.initTickableEvents();
17519             return;
17520         }
17521         
17522         Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17523         
17524         if(this.hiddenName){
17525             
17526             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17527             
17528             this.hiddenField.dom.value =
17529                 this.hiddenValue !== undefined ? this.hiddenValue :
17530                 this.value !== undefined ? this.value : '';
17531
17532             // prevent input submission
17533             this.el.dom.removeAttribute('name');
17534             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17535              
17536              
17537         }
17538         //if(Roo.isGecko){
17539         //    this.el.dom.setAttribute('autocomplete', 'off');
17540         //}
17541         
17542         var cls = 'x-combo-list';
17543         
17544         //this.list = new Roo.Layer({
17545         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17546         //});
17547         
17548         var _this = this;
17549         
17550         (function(){
17551             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17552             _this.list.setWidth(lw);
17553         }).defer(100);
17554         
17555         this.list.on('mouseover', this.onViewOver, this);
17556         this.list.on('mousemove', this.onViewMove, this);
17557         this.list.on('scroll', this.onViewScroll, this);
17558         
17559         /*
17560         this.list.swallowEvent('mousewheel');
17561         this.assetHeight = 0;
17562
17563         if(this.title){
17564             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17565             this.assetHeight += this.header.getHeight();
17566         }
17567
17568         this.innerList = this.list.createChild({cls:cls+'-inner'});
17569         this.innerList.on('mouseover', this.onViewOver, this);
17570         this.innerList.on('mousemove', this.onViewMove, this);
17571         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17572         
17573         if(this.allowBlank && !this.pageSize && !this.disableClear){
17574             this.footer = this.list.createChild({cls:cls+'-ft'});
17575             this.pageTb = new Roo.Toolbar(this.footer);
17576            
17577         }
17578         if(this.pageSize){
17579             this.footer = this.list.createChild({cls:cls+'-ft'});
17580             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17581                     {pageSize: this.pageSize});
17582             
17583         }
17584         
17585         if (this.pageTb && this.allowBlank && !this.disableClear) {
17586             var _this = this;
17587             this.pageTb.add(new Roo.Toolbar.Fill(), {
17588                 cls: 'x-btn-icon x-btn-clear',
17589                 text: '&#160;',
17590                 handler: function()
17591                 {
17592                     _this.collapse();
17593                     _this.clearValue();
17594                     _this.onSelect(false, -1);
17595                 }
17596             });
17597         }
17598         if (this.footer) {
17599             this.assetHeight += this.footer.getHeight();
17600         }
17601         */
17602             
17603         if(!this.tpl){
17604             this.tpl = Roo.bootstrap.version == 4 ?
17605                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17606                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17607         }
17608
17609         this.view = new Roo.View(this.list, this.tpl, {
17610             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17611         });
17612         //this.view.wrapEl.setDisplayed(false);
17613         this.view.on('click', this.onViewClick, this);
17614         
17615         
17616         this.store.on('beforeload', this.onBeforeLoad, this);
17617         this.store.on('load', this.onLoad, this);
17618         this.store.on('loadexception', this.onLoadException, this);
17619         /*
17620         if(this.resizable){
17621             this.resizer = new Roo.Resizable(this.list,  {
17622                pinned:true, handles:'se'
17623             });
17624             this.resizer.on('resize', function(r, w, h){
17625                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17626                 this.listWidth = w;
17627                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17628                 this.restrictHeight();
17629             }, this);
17630             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17631         }
17632         */
17633         if(!this.editable){
17634             this.editable = true;
17635             this.setEditable(false);
17636         }
17637         
17638         /*
17639         
17640         if (typeof(this.events.add.listeners) != 'undefined') {
17641             
17642             this.addicon = this.wrap.createChild(
17643                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17644        
17645             this.addicon.on('click', function(e) {
17646                 this.fireEvent('add', this);
17647             }, this);
17648         }
17649         if (typeof(this.events.edit.listeners) != 'undefined') {
17650             
17651             this.editicon = this.wrap.createChild(
17652                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17653             if (this.addicon) {
17654                 this.editicon.setStyle('margin-left', '40px');
17655             }
17656             this.editicon.on('click', function(e) {
17657                 
17658                 // we fire even  if inothing is selected..
17659                 this.fireEvent('edit', this, this.lastData );
17660                 
17661             }, this);
17662         }
17663         */
17664         
17665         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17666             "up" : function(e){
17667                 this.inKeyMode = true;
17668                 this.selectPrev();
17669             },
17670
17671             "down" : function(e){
17672                 if(!this.isExpanded()){
17673                     this.onTriggerClick();
17674                 }else{
17675                     this.inKeyMode = true;
17676                     this.selectNext();
17677                 }
17678             },
17679
17680             "enter" : function(e){
17681 //                this.onViewClick();
17682                 //return true;
17683                 this.collapse();
17684                 
17685                 if(this.fireEvent("specialkey", this, e)){
17686                     this.onViewClick(false);
17687                 }
17688                 
17689                 return true;
17690             },
17691
17692             "esc" : function(e){
17693                 this.collapse();
17694             },
17695
17696             "tab" : function(e){
17697                 this.collapse();
17698                 
17699                 if(this.fireEvent("specialkey", this, e)){
17700                     this.onViewClick(false);
17701                 }
17702                 
17703                 return true;
17704             },
17705
17706             scope : this,
17707
17708             doRelay : function(foo, bar, hname){
17709                 if(hname == 'down' || this.scope.isExpanded()){
17710                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17711                 }
17712                 return true;
17713             },
17714
17715             forceKeyDown: true
17716         });
17717         
17718         
17719         this.queryDelay = Math.max(this.queryDelay || 10,
17720                 this.mode == 'local' ? 10 : 250);
17721         
17722         
17723         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17724         
17725         if(this.typeAhead){
17726             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17727         }
17728         if(this.editable !== false){
17729             this.inputEl().on("keyup", this.onKeyUp, this);
17730         }
17731         if(this.forceSelection){
17732             this.inputEl().on('blur', this.doForce, this);
17733         }
17734         
17735         if(this.multiple){
17736             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17737             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17738         }
17739     },
17740     
17741     initTickableEvents: function()
17742     {   
17743         this.createList();
17744         
17745         if(this.hiddenName){
17746             
17747             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17748             
17749             this.hiddenField.dom.value =
17750                 this.hiddenValue !== undefined ? this.hiddenValue :
17751                 this.value !== undefined ? this.value : '';
17752
17753             // prevent input submission
17754             this.el.dom.removeAttribute('name');
17755             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17756              
17757              
17758         }
17759         
17760 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17761         
17762         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17763         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17764         if(this.triggerList){
17765             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17766         }
17767          
17768         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17769         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17770         
17771         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17772         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17773         
17774         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17775         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17776         
17777         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17778         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17779         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17780         
17781         this.okBtn.hide();
17782         this.cancelBtn.hide();
17783         
17784         var _this = this;
17785         
17786         (function(){
17787             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17788             _this.list.setWidth(lw);
17789         }).defer(100);
17790         
17791         this.list.on('mouseover', this.onViewOver, this);
17792         this.list.on('mousemove', this.onViewMove, this);
17793         
17794         this.list.on('scroll', this.onViewScroll, this);
17795         
17796         if(!this.tpl){
17797             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17798                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17799         }
17800
17801         this.view = new Roo.View(this.list, this.tpl, {
17802             singleSelect:true,
17803             tickable:true,
17804             parent:this,
17805             store: this.store,
17806             selectedClass: this.selectedClass
17807         });
17808         
17809         //this.view.wrapEl.setDisplayed(false);
17810         this.view.on('click', this.onViewClick, this);
17811         
17812         
17813         
17814         this.store.on('beforeload', this.onBeforeLoad, this);
17815         this.store.on('load', this.onLoad, this);
17816         this.store.on('loadexception', this.onLoadException, this);
17817         
17818         if(this.editable){
17819             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17820                 "up" : function(e){
17821                     this.inKeyMode = true;
17822                     this.selectPrev();
17823                 },
17824
17825                 "down" : function(e){
17826                     this.inKeyMode = true;
17827                     this.selectNext();
17828                 },
17829
17830                 "enter" : function(e){
17831                     if(this.fireEvent("specialkey", this, e)){
17832                         this.onViewClick(false);
17833                     }
17834                     
17835                     return true;
17836                 },
17837
17838                 "esc" : function(e){
17839                     this.onTickableFooterButtonClick(e, false, false);
17840                 },
17841
17842                 "tab" : function(e){
17843                     this.fireEvent("specialkey", this, e);
17844                     
17845                     this.onTickableFooterButtonClick(e, false, false);
17846                     
17847                     return true;
17848                 },
17849
17850                 scope : this,
17851
17852                 doRelay : function(e, fn, key){
17853                     if(this.scope.isExpanded()){
17854                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17855                     }
17856                     return true;
17857                 },
17858
17859                 forceKeyDown: true
17860             });
17861         }
17862         
17863         this.queryDelay = Math.max(this.queryDelay || 10,
17864                 this.mode == 'local' ? 10 : 250);
17865         
17866         
17867         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17868         
17869         if(this.typeAhead){
17870             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17871         }
17872         
17873         if(this.editable !== false){
17874             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17875         }
17876         
17877         this.indicator = this.indicatorEl();
17878         
17879         if(this.indicator){
17880             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17881             this.indicator.hide();
17882         }
17883         
17884     },
17885
17886     onDestroy : function(){
17887         if(this.view){
17888             this.view.setStore(null);
17889             this.view.el.removeAllListeners();
17890             this.view.el.remove();
17891             this.view.purgeListeners();
17892         }
17893         if(this.list){
17894             this.list.dom.innerHTML  = '';
17895         }
17896         
17897         if(this.store){
17898             this.store.un('beforeload', this.onBeforeLoad, this);
17899             this.store.un('load', this.onLoad, this);
17900             this.store.un('loadexception', this.onLoadException, this);
17901         }
17902         Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17903     },
17904
17905     // private
17906     fireKey : function(e){
17907         if(e.isNavKeyPress() && !this.list.isVisible()){
17908             this.fireEvent("specialkey", this, e);
17909         }
17910     },
17911
17912     // private
17913     onResize: function(w, h)
17914     {
17915         
17916         
17917 //        Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17918 //        
17919 //        if(typeof w != 'number'){
17920 //            // we do not handle it!?!?
17921 //            return;
17922 //        }
17923 //        var tw = this.trigger.getWidth();
17924 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17925 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17926 //        var x = w - tw;
17927 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17928 //            
17929 //        //this.trigger.setStyle('left', x+'px');
17930 //        
17931 //        if(this.list && this.listWidth === undefined){
17932 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17933 //            this.list.setWidth(lw);
17934 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17935 //        }
17936         
17937     
17938         
17939     },
17940
17941     /**
17942      * Allow or prevent the user from directly editing the field text.  If false is passed,
17943      * the user will only be able to select from the items defined in the dropdown list.  This method
17944      * is the runtime equivalent of setting the 'editable' config option at config time.
17945      * @param {Boolean} value True to allow the user to directly edit the field text
17946      */
17947     setEditable : function(value){
17948         if(value == this.editable){
17949             return;
17950         }
17951         this.editable = value;
17952         if(!value){
17953             this.inputEl().dom.setAttribute('readOnly', true);
17954             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17955             this.inputEl().addClass('x-combo-noedit');
17956         }else{
17957             this.inputEl().dom.removeAttribute('readOnly');
17958             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17959             this.inputEl().removeClass('x-combo-noedit');
17960         }
17961     },
17962
17963     // private
17964     
17965     onBeforeLoad : function(combo,opts){
17966         if(!this.hasFocus){
17967             return;
17968         }
17969          if (!opts.add) {
17970             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17971          }
17972         this.restrictHeight();
17973         this.selectedIndex = -1;
17974     },
17975
17976     // private
17977     onLoad : function(){
17978         
17979         this.hasQuery = false;
17980         
17981         if(!this.hasFocus){
17982             return;
17983         }
17984         
17985         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17986             this.loading.hide();
17987         }
17988         
17989         if(this.store.getCount() > 0){
17990             
17991             this.expand();
17992             this.restrictHeight();
17993             if(this.lastQuery == this.allQuery){
17994                 if(this.editable && !this.tickable){
17995                     this.inputEl().dom.select();
17996                 }
17997                 
17998                 if(
17999                     !this.selectByValue(this.value, true) &&
18000                     this.autoFocus && 
18001                     (
18002                         !this.store.lastOptions ||
18003                         typeof(this.store.lastOptions.add) == 'undefined' || 
18004                         this.store.lastOptions.add != true
18005                     )
18006                 ){
18007                     this.select(0, true);
18008                 }
18009             }else{
18010                 if(this.autoFocus){
18011                     this.selectNext();
18012                 }
18013                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18014                     this.taTask.delay(this.typeAheadDelay);
18015                 }
18016             }
18017         }else{
18018             this.onEmptyResults();
18019         }
18020         
18021         //this.el.focus();
18022     },
18023     // private
18024     onLoadException : function()
18025     {
18026         this.hasQuery = false;
18027         
18028         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18029             this.loading.hide();
18030         }
18031         
18032         if(this.tickable && this.editable){
18033             return;
18034         }
18035         
18036         this.collapse();
18037         // only causes errors at present
18038         //Roo.log(this.store.reader.jsonData);
18039         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18040             // fixme
18041             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18042         //}
18043         
18044         
18045     },
18046     // private
18047     onTypeAhead : function(){
18048         if(this.store.getCount() > 0){
18049             var r = this.store.getAt(0);
18050             var newValue = r.data[this.displayField];
18051             var len = newValue.length;
18052             var selStart = this.getRawValue().length;
18053             
18054             if(selStart != len){
18055                 this.setRawValue(newValue);
18056                 this.selectText(selStart, newValue.length);
18057             }
18058         }
18059     },
18060
18061     // private
18062     onSelect : function(record, index){
18063         
18064         if(this.fireEvent('beforeselect', this, record, index) !== false){
18065         
18066             this.setFromData(index > -1 ? record.data : false);
18067             
18068             this.collapse();
18069             this.fireEvent('select', this, record, index);
18070         }
18071     },
18072
18073     /**
18074      * Returns the currently selected field value or empty string if no value is set.
18075      * @return {String} value The selected value
18076      */
18077     getValue : function()
18078     {
18079         if(Roo.isIOS && this.useNativeIOS){
18080             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18081         }
18082         
18083         if(this.multiple){
18084             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18085         }
18086         
18087         if(this.valueField){
18088             return typeof this.value != 'undefined' ? this.value : '';
18089         }else{
18090             return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18091         }
18092     },
18093     
18094     getRawValue : function()
18095     {
18096         if(Roo.isIOS && this.useNativeIOS){
18097             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18098         }
18099         
18100         var v = this.inputEl().getValue();
18101         
18102         return v;
18103     },
18104
18105     /**
18106      * Clears any text/value currently set in the field
18107      */
18108     clearValue : function(){
18109         
18110         if(this.hiddenField){
18111             this.hiddenField.dom.value = '';
18112         }
18113         this.value = '';
18114         this.setRawValue('');
18115         this.lastSelectionText = '';
18116         this.lastData = false;
18117         
18118         var close = this.closeTriggerEl();
18119         
18120         if(close){
18121             close.hide();
18122         }
18123         
18124         this.validate();
18125         
18126     },
18127
18128     /**
18129      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18130      * will be displayed in the field.  If the value does not match the data value of an existing item,
18131      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18132      * Otherwise the field will be blank (although the value will still be set).
18133      * @param {String} value The value to match
18134      */
18135     setValue : function(v)
18136     {
18137         if(Roo.isIOS && this.useNativeIOS){
18138             this.setIOSValue(v);
18139             return;
18140         }
18141         
18142         if(this.multiple){
18143             this.syncValue();
18144             return;
18145         }
18146         
18147         var text = v;
18148         if(this.valueField){
18149             var r = this.findRecord(this.valueField, v);
18150             if(r){
18151                 text = r.data[this.displayField];
18152             }else if(this.valueNotFoundText !== undefined){
18153                 text = this.valueNotFoundText;
18154             }
18155         }
18156         this.lastSelectionText = text;
18157         if(this.hiddenField){
18158             this.hiddenField.dom.value = v;
18159         }
18160         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18161         this.value = v;
18162         
18163         var close = this.closeTriggerEl();
18164         
18165         if(close){
18166             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18167         }
18168         
18169         this.validate();
18170     },
18171     /**
18172      * @property {Object} the last set data for the element
18173      */
18174     
18175     lastData : false,
18176     /**
18177      * Sets the value of the field based on a object which is related to the record format for the store.
18178      * @param {Object} value the value to set as. or false on reset?
18179      */
18180     setFromData : function(o){
18181         
18182         if(this.multiple){
18183             this.addItem(o);
18184             return;
18185         }
18186             
18187         var dv = ''; // display value
18188         var vv = ''; // value value..
18189         this.lastData = o;
18190         if (this.displayField) {
18191             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18192         } else {
18193             // this is an error condition!!!
18194             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18195         }
18196         
18197         if(this.valueField){
18198             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18199         }
18200         
18201         var close = this.closeTriggerEl();
18202         
18203         if(close){
18204             if(dv.length || vv * 1 > 0){
18205                 close.show() ;
18206                 this.blockFocus=true;
18207             } else {
18208                 close.hide();
18209             }             
18210         }
18211         
18212         if(this.hiddenField){
18213             this.hiddenField.dom.value = vv;
18214             
18215             this.lastSelectionText = dv;
18216             Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18217             this.value = vv;
18218             return;
18219         }
18220         // no hidden field.. - we store the value in 'value', but still display
18221         // display field!!!!
18222         this.lastSelectionText = dv;
18223         Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18224         this.value = vv;
18225         
18226         
18227         
18228     },
18229     // private
18230     reset : function(){
18231         // overridden so that last data is reset..
18232         
18233         if(this.multiple){
18234             this.clearItem();
18235             return;
18236         }
18237         
18238         this.setValue(this.originalValue);
18239         //this.clearInvalid();
18240         this.lastData = false;
18241         if (this.view) {
18242             this.view.clearSelections();
18243         }
18244         
18245         this.validate();
18246     },
18247     // private
18248     findRecord : function(prop, value){
18249         var record;
18250         if(this.store.getCount() > 0){
18251             this.store.each(function(r){
18252                 if(r.data[prop] == value){
18253                     record = r;
18254                     return false;
18255                 }
18256                 return true;
18257             });
18258         }
18259         return record;
18260     },
18261     
18262     getName: function()
18263     {
18264         // returns hidden if it's set..
18265         if (!this.rendered) {return ''};
18266         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
18267         
18268     },
18269     // private
18270     onViewMove : function(e, t){
18271         this.inKeyMode = false;
18272     },
18273
18274     // private
18275     onViewOver : function(e, t){
18276         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18277             return;
18278         }
18279         var item = this.view.findItemFromChild(t);
18280         
18281         if(item){
18282             var index = this.view.indexOf(item);
18283             this.select(index, false);
18284         }
18285     },
18286
18287     // private
18288     onViewClick : function(view, doFocus, el, e)
18289     {
18290         var index = this.view.getSelectedIndexes()[0];
18291         
18292         var r = this.store.getAt(index);
18293         
18294         if(this.tickable){
18295             
18296             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18297                 return;
18298             }
18299             
18300             var rm = false;
18301             var _this = this;
18302             
18303             Roo.each(this.tickItems, function(v,k){
18304                 
18305                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18306                     Roo.log(v);
18307                     _this.tickItems.splice(k, 1);
18308                     
18309                     if(typeof(e) == 'undefined' && view == false){
18310                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18311                     }
18312                     
18313                     rm = true;
18314                     return;
18315                 }
18316             });
18317             
18318             if(rm){
18319                 return;
18320             }
18321             
18322             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18323                 this.tickItems.push(r.data);
18324             }
18325             
18326             if(typeof(e) == 'undefined' && view == false){
18327                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18328             }
18329                     
18330             return;
18331         }
18332         
18333         if(r){
18334             this.onSelect(r, index);
18335         }
18336         if(doFocus !== false && !this.blockFocus){
18337             this.inputEl().focus();
18338         }
18339     },
18340
18341     // private
18342     restrictHeight : function(){
18343         //this.innerList.dom.style.height = '';
18344         //var inner = this.innerList.dom;
18345         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18346         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18347         //this.list.beginUpdate();
18348         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18349         this.list.alignTo(this.inputEl(), this.listAlign);
18350         this.list.alignTo(this.inputEl(), this.listAlign);
18351         //this.list.endUpdate();
18352     },
18353
18354     // private
18355     onEmptyResults : function(){
18356         
18357         if(this.tickable && this.editable){
18358             this.hasFocus = false;
18359             this.restrictHeight();
18360             return;
18361         }
18362         
18363         this.collapse();
18364     },
18365
18366     /**
18367      * Returns true if the dropdown list is expanded, else false.
18368      */
18369     isExpanded : function(){
18370         return this.list.isVisible();
18371     },
18372
18373     /**
18374      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18375      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18376      * @param {String} value The data value of the item to select
18377      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18378      * selected item if it is not currently in view (defaults to true)
18379      * @return {Boolean} True if the value matched an item in the list, else false
18380      */
18381     selectByValue : function(v, scrollIntoView){
18382         if(v !== undefined && v !== null){
18383             var r = this.findRecord(this.valueField || this.displayField, v);
18384             if(r){
18385                 this.select(this.store.indexOf(r), scrollIntoView);
18386                 return true;
18387             }
18388         }
18389         return false;
18390     },
18391
18392     /**
18393      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18394      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18395      * @param {Number} index The zero-based index of the list item to select
18396      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18397      * selected item if it is not currently in view (defaults to true)
18398      */
18399     select : function(index, scrollIntoView){
18400         this.selectedIndex = index;
18401         this.view.select(index);
18402         if(scrollIntoView !== false){
18403             var el = this.view.getNode(index);
18404             /*
18405              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18406              */
18407             if(el){
18408                 this.list.scrollChildIntoView(el, false);
18409             }
18410         }
18411     },
18412
18413     // private
18414     selectNext : function(){
18415         var ct = this.store.getCount();
18416         if(ct > 0){
18417             if(this.selectedIndex == -1){
18418                 this.select(0);
18419             }else if(this.selectedIndex < ct-1){
18420                 this.select(this.selectedIndex+1);
18421             }
18422         }
18423     },
18424
18425     // private
18426     selectPrev : function(){
18427         var ct = this.store.getCount();
18428         if(ct > 0){
18429             if(this.selectedIndex == -1){
18430                 this.select(0);
18431             }else if(this.selectedIndex != 0){
18432                 this.select(this.selectedIndex-1);
18433             }
18434         }
18435     },
18436
18437     // private
18438     onKeyUp : function(e){
18439         if(this.editable !== false && !e.isSpecialKey()){
18440             this.lastKey = e.getKey();
18441             this.dqTask.delay(this.queryDelay);
18442         }
18443     },
18444
18445     // private
18446     validateBlur : function(){
18447         return !this.list || !this.list.isVisible();   
18448     },
18449
18450     // private
18451     initQuery : function(){
18452         
18453         var v = this.getRawValue();
18454         
18455         if(this.tickable && this.editable){
18456             v = this.tickableInputEl().getValue();
18457         }
18458         
18459         this.doQuery(v);
18460     },
18461
18462     // private
18463     doForce : function(){
18464         if(this.inputEl().dom.value.length > 0){
18465             this.inputEl().dom.value =
18466                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18467              
18468         }
18469     },
18470
18471     /**
18472      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
18473      * query allowing the query action to be canceled if needed.
18474      * @param {String} query The SQL query to execute
18475      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18476      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
18477      * saved in the current store (defaults to false)
18478      */
18479     doQuery : function(q, forceAll){
18480         
18481         if(q === undefined || q === null){
18482             q = '';
18483         }
18484         var qe = {
18485             query: q,
18486             forceAll: forceAll,
18487             combo: this,
18488             cancel:false
18489         };
18490         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18491             return false;
18492         }
18493         q = qe.query;
18494         
18495         forceAll = qe.forceAll;
18496         if(forceAll === true || (q.length >= this.minChars)){
18497             
18498             this.hasQuery = true;
18499             
18500             if(this.lastQuery != q || this.alwaysQuery){
18501                 this.lastQuery = q;
18502                 if(this.mode == 'local'){
18503                     this.selectedIndex = -1;
18504                     if(forceAll){
18505                         this.store.clearFilter();
18506                     }else{
18507                         
18508                         if(this.specialFilter){
18509                             this.fireEvent('specialfilter', this);
18510                             this.onLoad();
18511                             return;
18512                         }
18513                         
18514                         this.store.filter(this.displayField, q);
18515                     }
18516                     
18517                     this.store.fireEvent("datachanged", this.store);
18518                     
18519                     this.onLoad();
18520                     
18521                     
18522                 }else{
18523                     
18524                     this.store.baseParams[this.queryParam] = q;
18525                     
18526                     var options = {params : this.getParams(q)};
18527                     
18528                     if(this.loadNext){
18529                         options.add = true;
18530                         options.params.start = this.page * this.pageSize;
18531                     }
18532                     
18533                     this.store.load(options);
18534                     
18535                     /*
18536                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18537                      *  we should expand the list on onLoad
18538                      *  so command out it
18539                      */
18540 //                    this.expand();
18541                 }
18542             }else{
18543                 this.selectedIndex = -1;
18544                 this.onLoad();   
18545             }
18546         }
18547         
18548         this.loadNext = false;
18549     },
18550     
18551     // private
18552     getParams : function(q){
18553         var p = {};
18554         //p[this.queryParam] = q;
18555         
18556         if(this.pageSize){
18557             p.start = 0;
18558             p.limit = this.pageSize;
18559         }
18560         return p;
18561     },
18562
18563     /**
18564      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18565      */
18566     collapse : function(){
18567         if(!this.isExpanded()){
18568             return;
18569         }
18570         
18571         this.list.hide();
18572         
18573         this.hasFocus = false;
18574         
18575         if(this.tickable){
18576             this.okBtn.hide();
18577             this.cancelBtn.hide();
18578             this.trigger.show();
18579             
18580             if(this.editable){
18581                 this.tickableInputEl().dom.value = '';
18582                 this.tickableInputEl().blur();
18583             }
18584             
18585         }
18586         
18587         Roo.get(document).un('mousedown', this.collapseIf, this);
18588         Roo.get(document).un('mousewheel', this.collapseIf, this);
18589         if (!this.editable) {
18590             Roo.get(document).un('keydown', this.listKeyPress, this);
18591         }
18592         this.fireEvent('collapse', this);
18593         
18594         this.validate();
18595     },
18596
18597     // private
18598     collapseIf : function(e){
18599         var in_combo  = e.within(this.el);
18600         var in_list =  e.within(this.list);
18601         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18602         
18603         if (in_combo || in_list || is_list) {
18604             //e.stopPropagation();
18605             return;
18606         }
18607         
18608         if(this.tickable){
18609             this.onTickableFooterButtonClick(e, false, false);
18610         }
18611
18612         this.collapse();
18613         
18614     },
18615
18616     /**
18617      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18618      */
18619     expand : function(){
18620        
18621         if(this.isExpanded() || !this.hasFocus){
18622             return;
18623         }
18624         
18625         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18626         this.list.setWidth(lw);
18627         
18628         Roo.log('expand');
18629         
18630         this.list.show();
18631         
18632         this.restrictHeight();
18633         
18634         if(this.tickable){
18635             
18636             this.tickItems = Roo.apply([], this.item);
18637             
18638             this.okBtn.show();
18639             this.cancelBtn.show();
18640             this.trigger.hide();
18641             
18642             if(this.editable){
18643                 this.tickableInputEl().focus();
18644             }
18645             
18646         }
18647         
18648         Roo.get(document).on('mousedown', this.collapseIf, this);
18649         Roo.get(document).on('mousewheel', this.collapseIf, this);
18650         if (!this.editable) {
18651             Roo.get(document).on('keydown', this.listKeyPress, this);
18652         }
18653         
18654         this.fireEvent('expand', this);
18655     },
18656
18657     // private
18658     // Implements the default empty TriggerField.onTriggerClick function
18659     onTriggerClick : function(e)
18660     {
18661         Roo.log('trigger click');
18662         
18663         if(this.disabled || !this.triggerList){
18664             return;
18665         }
18666         
18667         this.page = 0;
18668         this.loadNext = false;
18669         
18670         if(this.isExpanded()){
18671             this.collapse();
18672             if (!this.blockFocus) {
18673                 this.inputEl().focus();
18674             }
18675             
18676         }else {
18677             this.hasFocus = true;
18678             if(this.triggerAction == 'all') {
18679                 this.doQuery(this.allQuery, true);
18680             } else {
18681                 this.doQuery(this.getRawValue());
18682             }
18683             if (!this.blockFocus) {
18684                 this.inputEl().focus();
18685             }
18686         }
18687     },
18688     
18689     onTickableTriggerClick : function(e)
18690     {
18691         if(this.disabled){
18692             return;
18693         }
18694         
18695         this.page = 0;
18696         this.loadNext = false;
18697         this.hasFocus = true;
18698         
18699         if(this.triggerAction == 'all') {
18700             this.doQuery(this.allQuery, true);
18701         } else {
18702             this.doQuery(this.getRawValue());
18703         }
18704     },
18705     
18706     onSearchFieldClick : function(e)
18707     {
18708         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18709             this.onTickableFooterButtonClick(e, false, false);
18710             return;
18711         }
18712         
18713         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18714             return;
18715         }
18716         
18717         this.page = 0;
18718         this.loadNext = false;
18719         this.hasFocus = true;
18720         
18721         if(this.triggerAction == 'all') {
18722             this.doQuery(this.allQuery, true);
18723         } else {
18724             this.doQuery(this.getRawValue());
18725         }
18726     },
18727     
18728     listKeyPress : function(e)
18729     {
18730         //Roo.log('listkeypress');
18731         // scroll to first matching element based on key pres..
18732         if (e.isSpecialKey()) {
18733             return false;
18734         }
18735         var k = String.fromCharCode(e.getKey()).toUpperCase();
18736         //Roo.log(k);
18737         var match  = false;
18738         var csel = this.view.getSelectedNodes();
18739         var cselitem = false;
18740         if (csel.length) {
18741             var ix = this.view.indexOf(csel[0]);
18742             cselitem  = this.store.getAt(ix);
18743             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18744                 cselitem = false;
18745             }
18746             
18747         }
18748         
18749         this.store.each(function(v) { 
18750             if (cselitem) {
18751                 // start at existing selection.
18752                 if (cselitem.id == v.id) {
18753                     cselitem = false;
18754                 }
18755                 return true;
18756             }
18757                 
18758             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18759                 match = this.store.indexOf(v);
18760                 return false;
18761             }
18762             return true;
18763         }, this);
18764         
18765         if (match === false) {
18766             return true; // no more action?
18767         }
18768         // scroll to?
18769         this.view.select(match);
18770         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18771         sn.scrollIntoView(sn.dom.parentNode, false);
18772     },
18773     
18774     onViewScroll : function(e, t){
18775         
18776         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){
18777             return;
18778         }
18779         
18780         this.hasQuery = true;
18781         
18782         this.loading = this.list.select('.loading', true).first();
18783         
18784         if(this.loading === null){
18785             this.list.createChild({
18786                 tag: 'div',
18787                 cls: 'loading roo-select2-more-results roo-select2-active',
18788                 html: 'Loading more results...'
18789             });
18790             
18791             this.loading = this.list.select('.loading', true).first();
18792             
18793             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18794             
18795             this.loading.hide();
18796         }
18797         
18798         this.loading.show();
18799         
18800         var _combo = this;
18801         
18802         this.page++;
18803         this.loadNext = true;
18804         
18805         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18806         
18807         return;
18808     },
18809     
18810     addItem : function(o)
18811     {   
18812         var dv = ''; // display value
18813         
18814         if (this.displayField) {
18815             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18816         } else {
18817             // this is an error condition!!!
18818             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18819         }
18820         
18821         if(!dv.length){
18822             return;
18823         }
18824         
18825         var choice = this.choices.createChild({
18826             tag: 'li',
18827             cls: 'roo-select2-search-choice',
18828             cn: [
18829                 {
18830                     tag: 'div',
18831                     html: dv
18832                 },
18833                 {
18834                     tag: 'a',
18835                     href: '#',
18836                     cls: 'roo-select2-search-choice-close fa fa-times',
18837                     tabindex: '-1'
18838                 }
18839             ]
18840             
18841         }, this.searchField);
18842         
18843         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18844         
18845         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18846         
18847         this.item.push(o);
18848         
18849         this.lastData = o;
18850         
18851         this.syncValue();
18852         
18853         this.inputEl().dom.value = '';
18854         
18855         this.validate();
18856     },
18857     
18858     onRemoveItem : function(e, _self, o)
18859     {
18860         e.preventDefault();
18861         
18862         this.lastItem = Roo.apply([], this.item);
18863         
18864         var index = this.item.indexOf(o.data) * 1;
18865         
18866         if( index < 0){
18867             Roo.log('not this item?!');
18868             return;
18869         }
18870         
18871         this.item.splice(index, 1);
18872         o.item.remove();
18873         
18874         this.syncValue();
18875         
18876         this.fireEvent('remove', this, e);
18877         
18878         this.validate();
18879         
18880     },
18881     
18882     syncValue : function()
18883     {
18884         if(!this.item.length){
18885             this.clearValue();
18886             return;
18887         }
18888             
18889         var value = [];
18890         var _this = this;
18891         Roo.each(this.item, function(i){
18892             if(_this.valueField){
18893                 value.push(i[_this.valueField]);
18894                 return;
18895             }
18896
18897             value.push(i);
18898         });
18899
18900         this.value = value.join(',');
18901
18902         if(this.hiddenField){
18903             this.hiddenField.dom.value = this.value;
18904         }
18905         
18906         this.store.fireEvent("datachanged", this.store);
18907         
18908         this.validate();
18909     },
18910     
18911     clearItem : function()
18912     {
18913         if(!this.multiple){
18914             return;
18915         }
18916         
18917         this.item = [];
18918         
18919         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18920            c.remove();
18921         });
18922         
18923         this.syncValue();
18924         
18925         this.validate();
18926         
18927         if(this.tickable && !Roo.isTouch){
18928             this.view.refresh();
18929         }
18930     },
18931     
18932     inputEl: function ()
18933     {
18934         if(Roo.isIOS && this.useNativeIOS){
18935             return this.el.select('select.roo-ios-select', true).first();
18936         }
18937         
18938         if(Roo.isTouch && this.mobileTouchView){
18939             return this.el.select('input.form-control',true).first();
18940         }
18941         
18942         if(this.tickable){
18943             return this.searchField;
18944         }
18945         
18946         return this.el.select('input.form-control',true).first();
18947     },
18948     
18949     onTickableFooterButtonClick : function(e, btn, el)
18950     {
18951         e.preventDefault();
18952         
18953         this.lastItem = Roo.apply([], this.item);
18954         
18955         if(btn && btn.name == 'cancel'){
18956             this.tickItems = Roo.apply([], this.item);
18957             this.collapse();
18958             return;
18959         }
18960         
18961         this.clearItem();
18962         
18963         var _this = this;
18964         
18965         Roo.each(this.tickItems, function(o){
18966             _this.addItem(o);
18967         });
18968         
18969         this.collapse();
18970         
18971     },
18972     
18973     validate : function()
18974     {
18975         if(this.getVisibilityEl().hasClass('hidden')){
18976             return true;
18977         }
18978         
18979         var v = this.getRawValue();
18980         
18981         if(this.multiple){
18982             v = this.getValue();
18983         }
18984         
18985         if(this.disabled || this.allowBlank || v.length){
18986             this.markValid();
18987             return true;
18988         }
18989         
18990         this.markInvalid();
18991         return false;
18992     },
18993     
18994     tickableInputEl : function()
18995     {
18996         if(!this.tickable || !this.editable){
18997             return this.inputEl();
18998         }
18999         
19000         return this.inputEl().select('.roo-select2-search-field-input', true).first();
19001     },
19002     
19003     
19004     getAutoCreateTouchView : function()
19005     {
19006         var id = Roo.id();
19007         
19008         var cfg = {
19009             cls: 'form-group' //input-group
19010         };
19011         
19012         var input =  {
19013             tag: 'input',
19014             id : id,
19015             type : this.inputType,
19016             cls : 'form-control x-combo-noedit',
19017             autocomplete: 'new-password',
19018             placeholder : this.placeholder || '',
19019             readonly : true
19020         };
19021         
19022         if (this.name) {
19023             input.name = this.name;
19024         }
19025         
19026         if (this.size) {
19027             input.cls += ' input-' + this.size;
19028         }
19029         
19030         if (this.disabled) {
19031             input.disabled = true;
19032         }
19033         
19034         var inputblock = {
19035             cls : 'roo-combobox-wrap',
19036             cn : [
19037                 input
19038             ]
19039         };
19040         
19041         if(this.before){
19042             inputblock.cls += ' input-group';
19043             
19044             inputblock.cn.unshift({
19045                 tag :'span',
19046                 cls : 'input-group-addon input-group-prepend input-group-text',
19047                 html : this.before
19048             });
19049         }
19050         
19051         if(this.removable && !this.multiple){
19052             inputblock.cls += ' roo-removable';
19053             
19054             inputblock.cn.push({
19055                 tag: 'button',
19056                 html : 'x',
19057                 cls : 'roo-combo-removable-btn close'
19058             });
19059         }
19060
19061         if(this.hasFeedback && !this.allowBlank){
19062             
19063             inputblock.cls += ' has-feedback';
19064             
19065             inputblock.cn.push({
19066                 tag: 'span',
19067                 cls: 'glyphicon form-control-feedback'
19068             });
19069             
19070         }
19071         
19072         if (this.after) {
19073             
19074             inputblock.cls += (this.before) ? '' : ' input-group';
19075             
19076             inputblock.cn.push({
19077                 tag :'span',
19078                 cls : 'input-group-addon input-group-append input-group-text',
19079                 html : this.after
19080             });
19081         }
19082
19083         
19084         var ibwrap = inputblock;
19085         
19086         if(this.multiple){
19087             ibwrap = {
19088                 tag: 'ul',
19089                 cls: 'roo-select2-choices',
19090                 cn:[
19091                     {
19092                         tag: 'li',
19093                         cls: 'roo-select2-search-field',
19094                         cn: [
19095
19096                             inputblock
19097                         ]
19098                     }
19099                 ]
19100             };
19101         
19102             
19103         }
19104         
19105         var combobox = {
19106             cls: 'roo-select2-container input-group roo-touchview-combobox ',
19107             cn: [
19108                 {
19109                     tag: 'input',
19110                     type : 'hidden',
19111                     cls: 'form-hidden-field'
19112                 },
19113                 ibwrap
19114             ]
19115         };
19116         
19117         if(!this.multiple && this.showToggleBtn){
19118             
19119             var caret = {
19120                 cls: 'caret'
19121             };
19122             
19123             if (this.caret != false) {
19124                 caret = {
19125                      tag: 'i',
19126                      cls: 'fa fa-' + this.caret
19127                 };
19128                 
19129             }
19130             
19131             combobox.cn.push({
19132                 tag :'span',
19133                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19134                 cn : [
19135                     Roo.bootstrap.version == 3 ? caret : '',
19136                     {
19137                         tag: 'span',
19138                         cls: 'combobox-clear',
19139                         cn  : [
19140                             {
19141                                 tag : 'i',
19142                                 cls: 'icon-remove'
19143                             }
19144                         ]
19145                     }
19146                 ]
19147
19148             })
19149         }
19150         
19151         if(this.multiple){
19152             combobox.cls += ' roo-select2-container-multi';
19153         }
19154         
19155         var required =  this.allowBlank ?  {
19156                     tag : 'i',
19157                     style: 'display: none'
19158                 } : {
19159                    tag : 'i',
19160                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19161                    tooltip : 'This field is required'
19162                 };
19163         
19164         var align = this.labelAlign || this.parentLabelAlign();
19165         
19166         if (align ==='left' && this.fieldLabel.length) {
19167
19168             cfg.cn = [
19169                 required,
19170                 {
19171                     tag: 'label',
19172                     cls : 'control-label col-form-label',
19173                     html : this.fieldLabel
19174
19175                 },
19176                 {
19177                     cls : 'roo-combobox-wrap ', 
19178                     cn: [
19179                         combobox
19180                     ]
19181                 }
19182             ];
19183             
19184             var labelCfg = cfg.cn[1];
19185             var contentCfg = cfg.cn[2];
19186             
19187
19188             if(this.indicatorpos == 'right'){
19189                 cfg.cn = [
19190                     {
19191                         tag: 'label',
19192                         'for' :  id,
19193                         cls : 'control-label col-form-label',
19194                         cn : [
19195                             {
19196                                 tag : 'span',
19197                                 html : this.fieldLabel
19198                             },
19199                             required
19200                         ]
19201                     },
19202                     {
19203                         cls : "roo-combobox-wrap ",
19204                         cn: [
19205                             combobox
19206                         ]
19207                     }
19208
19209                 ];
19210                 
19211                 labelCfg = cfg.cn[0];
19212                 contentCfg = cfg.cn[1];
19213             }
19214             
19215            
19216             
19217             if(this.labelWidth > 12){
19218                 labelCfg.style = "width: " + this.labelWidth + 'px';
19219             }
19220            
19221             if(this.labelWidth < 13 && this.labelmd == 0){
19222                 this.labelmd = this.labelWidth;
19223             }
19224             
19225             if(this.labellg > 0){
19226                 labelCfg.cls += ' col-lg-' + this.labellg;
19227                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19228             }
19229             
19230             if(this.labelmd > 0){
19231                 labelCfg.cls += ' col-md-' + this.labelmd;
19232                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19233             }
19234             
19235             if(this.labelsm > 0){
19236                 labelCfg.cls += ' col-sm-' + this.labelsm;
19237                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19238             }
19239             
19240             if(this.labelxs > 0){
19241                 labelCfg.cls += ' col-xs-' + this.labelxs;
19242                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19243             }
19244                 
19245                 
19246         } else if ( this.fieldLabel.length) {
19247             cfg.cn = [
19248                required,
19249                 {
19250                     tag: 'label',
19251                     cls : 'control-label',
19252                     html : this.fieldLabel
19253
19254                 },
19255                 {
19256                     cls : '', 
19257                     cn: [
19258                         combobox
19259                     ]
19260                 }
19261             ];
19262             
19263             if(this.indicatorpos == 'right'){
19264                 cfg.cn = [
19265                     {
19266                         tag: 'label',
19267                         cls : 'control-label',
19268                         html : this.fieldLabel,
19269                         cn : [
19270                             required
19271                         ]
19272                     },
19273                     {
19274                         cls : '', 
19275                         cn: [
19276                             combobox
19277                         ]
19278                     }
19279                 ];
19280             }
19281         } else {
19282             cfg.cn = combobox;    
19283         }
19284         
19285         
19286         var settings = this;
19287         
19288         ['xs','sm','md','lg'].map(function(size){
19289             if (settings[size]) {
19290                 cfg.cls += ' col-' + size + '-' + settings[size];
19291             }
19292         });
19293         
19294         return cfg;
19295     },
19296     
19297     initTouchView : function()
19298     {
19299         this.renderTouchView();
19300         
19301         this.touchViewEl.on('scroll', function(){
19302             this.el.dom.scrollTop = 0;
19303         }, this);
19304         
19305         this.originalValue = this.getValue();
19306         
19307         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19308         
19309         this.inputEl().on("click", this.showTouchView, this);
19310         if (this.triggerEl) {
19311             this.triggerEl.on("click", this.showTouchView, this);
19312         }
19313         
19314         
19315         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19316         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19317         
19318         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19319         
19320         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19321         this.store.on('load', this.onTouchViewLoad, this);
19322         this.store.on('loadexception', this.onTouchViewLoadException, this);
19323         
19324         if(this.hiddenName){
19325             
19326             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19327             
19328             this.hiddenField.dom.value =
19329                 this.hiddenValue !== undefined ? this.hiddenValue :
19330                 this.value !== undefined ? this.value : '';
19331         
19332             this.el.dom.removeAttribute('name');
19333             this.hiddenField.dom.setAttribute('name', this.hiddenName);
19334         }
19335         
19336         if(this.multiple){
19337             this.choices = this.el.select('ul.roo-select2-choices', true).first();
19338             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19339         }
19340         
19341         if(this.removable && !this.multiple){
19342             var close = this.closeTriggerEl();
19343             if(close){
19344                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19345                 close.on('click', this.removeBtnClick, this, close);
19346             }
19347         }
19348         /*
19349          * fix the bug in Safari iOS8
19350          */
19351         this.inputEl().on("focus", function(e){
19352             document.activeElement.blur();
19353         }, this);
19354         
19355         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19356         
19357         return;
19358         
19359         
19360     },
19361     
19362     renderTouchView : function()
19363     {
19364         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19365         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19366         
19367         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19368         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19369         
19370         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19371         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372         this.touchViewBodyEl.setStyle('overflow', 'auto');
19373         
19374         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19375         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19376         
19377         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19378         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19379         
19380     },
19381     
19382     showTouchView : function()
19383     {
19384         if(this.disabled){
19385             return;
19386         }
19387         
19388         this.touchViewHeaderEl.hide();
19389
19390         if(this.modalTitle.length){
19391             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19392             this.touchViewHeaderEl.show();
19393         }
19394
19395         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19396         this.touchViewEl.show();
19397
19398         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19399         
19400         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19401         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19402
19403         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19404
19405         if(this.modalTitle.length){
19406             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19407         }
19408         
19409         this.touchViewBodyEl.setHeight(bodyHeight);
19410
19411         if(this.animate){
19412             var _this = this;
19413             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19414         }else{
19415             this.touchViewEl.addClass(['in','show']);
19416         }
19417         
19418         if(this._touchViewMask){
19419             Roo.get(document.body).addClass("x-body-masked");
19420             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19421             this._touchViewMask.setStyle('z-index', 10000);
19422             this._touchViewMask.addClass('show');
19423         }
19424         
19425         this.doTouchViewQuery();
19426         
19427     },
19428     
19429     hideTouchView : function()
19430     {
19431         this.touchViewEl.removeClass(['in','show']);
19432
19433         if(this.animate){
19434             var _this = this;
19435             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19436         }else{
19437             this.touchViewEl.setStyle('display', 'none');
19438         }
19439         
19440         if(this._touchViewMask){
19441             this._touchViewMask.removeClass('show');
19442             Roo.get(document.body).removeClass("x-body-masked");
19443         }
19444     },
19445     
19446     setTouchViewValue : function()
19447     {
19448         if(this.multiple){
19449             this.clearItem();
19450         
19451             var _this = this;
19452
19453             Roo.each(this.tickItems, function(o){
19454                 this.addItem(o);
19455             }, this);
19456         }
19457         
19458         this.hideTouchView();
19459     },
19460     
19461     doTouchViewQuery : function()
19462     {
19463         var qe = {
19464             query: '',
19465             forceAll: true,
19466             combo: this,
19467             cancel:false
19468         };
19469         
19470         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19471             return false;
19472         }
19473         
19474         if(!this.alwaysQuery || this.mode == 'local'){
19475             this.onTouchViewLoad();
19476             return;
19477         }
19478         
19479         this.store.load();
19480     },
19481     
19482     onTouchViewBeforeLoad : function(combo,opts)
19483     {
19484         return;
19485     },
19486
19487     // private
19488     onTouchViewLoad : function()
19489     {
19490         if(this.store.getCount() < 1){
19491             this.onTouchViewEmptyResults();
19492             return;
19493         }
19494         
19495         this.clearTouchView();
19496         
19497         var rawValue = this.getRawValue();
19498         
19499         var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19500         
19501         this.tickItems = [];
19502         
19503         this.store.data.each(function(d, rowIndex){
19504             var row = this.touchViewListGroup.createChild(template);
19505             
19506             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19507                 row.addClass(d.data.cls);
19508             }
19509             
19510             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19511                 var cfg = {
19512                     data : d.data,
19513                     html : d.data[this.displayField]
19514                 };
19515                 
19516                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19517                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19518                 }
19519             }
19520             row.removeClass('selected');
19521             if(!this.multiple && this.valueField &&
19522                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19523             {
19524                 // radio buttons..
19525                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19526                 row.addClass('selected');
19527             }
19528             
19529             if(this.multiple && this.valueField &&
19530                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19531             {
19532                 
19533                 // checkboxes...
19534                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19535                 this.tickItems.push(d.data);
19536             }
19537             
19538             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19539             
19540         }, this);
19541         
19542         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19543         
19544         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19545
19546         if(this.modalTitle.length){
19547             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19548         }
19549
19550         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19551         
19552         if(this.mobile_restrict_height && listHeight < bodyHeight){
19553             this.touchViewBodyEl.setHeight(listHeight);
19554         }
19555         
19556         var _this = this;
19557         
19558         if(firstChecked && listHeight > bodyHeight){
19559             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19560         }
19561         
19562     },
19563     
19564     onTouchViewLoadException : function()
19565     {
19566         this.hideTouchView();
19567     },
19568     
19569     onTouchViewEmptyResults : function()
19570     {
19571         this.clearTouchView();
19572         
19573         this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19574         
19575         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19576         
19577     },
19578     
19579     clearTouchView : function()
19580     {
19581         this.touchViewListGroup.dom.innerHTML = '';
19582     },
19583     
19584     onTouchViewClick : function(e, el, o)
19585     {
19586         e.preventDefault();
19587         
19588         var row = o.row;
19589         var rowIndex = o.rowIndex;
19590         
19591         var r = this.store.getAt(rowIndex);
19592         
19593         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19594             
19595             if(!this.multiple){
19596                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19597                     c.dom.removeAttribute('checked');
19598                 }, this);
19599
19600                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19601
19602                 this.setFromData(r.data);
19603
19604                 var close = this.closeTriggerEl();
19605
19606                 if(close){
19607                     close.show();
19608                 }
19609
19610                 this.hideTouchView();
19611
19612                 this.fireEvent('select', this, r, rowIndex);
19613
19614                 return;
19615             }
19616
19617             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19618                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19619                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19620                 return;
19621             }
19622
19623             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19624             this.addItem(r.data);
19625             this.tickItems.push(r.data);
19626         }
19627     },
19628     
19629     getAutoCreateNativeIOS : function()
19630     {
19631         var cfg = {
19632             cls: 'form-group' //input-group,
19633         };
19634         
19635         var combobox =  {
19636             tag: 'select',
19637             cls : 'roo-ios-select'
19638         };
19639         
19640         if (this.name) {
19641             combobox.name = this.name;
19642         }
19643         
19644         if (this.disabled) {
19645             combobox.disabled = true;
19646         }
19647         
19648         var settings = this;
19649         
19650         ['xs','sm','md','lg'].map(function(size){
19651             if (settings[size]) {
19652                 cfg.cls += ' col-' + size + '-' + settings[size];
19653             }
19654         });
19655         
19656         cfg.cn = combobox;
19657         
19658         return cfg;
19659         
19660     },
19661     
19662     initIOSView : function()
19663     {
19664         this.store.on('load', this.onIOSViewLoad, this);
19665         
19666         return;
19667     },
19668     
19669     onIOSViewLoad : function()
19670     {
19671         if(this.store.getCount() < 1){
19672             return;
19673         }
19674         
19675         this.clearIOSView();
19676         
19677         if(this.allowBlank) {
19678             
19679             var default_text = '-- SELECT --';
19680             
19681             if(this.placeholder.length){
19682                 default_text = this.placeholder;
19683             }
19684             
19685             if(this.emptyTitle.length){
19686                 default_text += ' - ' + this.emptyTitle + ' -';
19687             }
19688             
19689             var opt = this.inputEl().createChild({
19690                 tag: 'option',
19691                 value : 0,
19692                 html : default_text
19693             });
19694             
19695             var o = {};
19696             o[this.valueField] = 0;
19697             o[this.displayField] = default_text;
19698             
19699             this.ios_options.push({
19700                 data : o,
19701                 el : opt
19702             });
19703             
19704         }
19705         
19706         this.store.data.each(function(d, rowIndex){
19707             
19708             var html = '';
19709             
19710             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19711                 html = d.data[this.displayField];
19712             }
19713             
19714             var value = '';
19715             
19716             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19717                 value = d.data[this.valueField];
19718             }
19719             
19720             var option = {
19721                 tag: 'option',
19722                 value : value,
19723                 html : html
19724             };
19725             
19726             if(this.value == d.data[this.valueField]){
19727                 option['selected'] = true;
19728             }
19729             
19730             var opt = this.inputEl().createChild(option);
19731             
19732             this.ios_options.push({
19733                 data : d.data,
19734                 el : opt
19735             });
19736             
19737         }, this);
19738         
19739         this.inputEl().on('change', function(){
19740            this.fireEvent('select', this);
19741         }, this);
19742         
19743     },
19744     
19745     clearIOSView: function()
19746     {
19747         this.inputEl().dom.innerHTML = '';
19748         
19749         this.ios_options = [];
19750     },
19751     
19752     setIOSValue: function(v)
19753     {
19754         this.value = v;
19755         
19756         if(!this.ios_options){
19757             return;
19758         }
19759         
19760         Roo.each(this.ios_options, function(opts){
19761            
19762            opts.el.dom.removeAttribute('selected');
19763            
19764            if(opts.data[this.valueField] != v){
19765                return;
19766            }
19767            
19768            opts.el.dom.setAttribute('selected', true);
19769            
19770         }, this);
19771     }
19772
19773     /** 
19774     * @cfg {Boolean} grow 
19775     * @hide 
19776     */
19777     /** 
19778     * @cfg {Number} growMin 
19779     * @hide 
19780     */
19781     /** 
19782     * @cfg {Number} growMax 
19783     * @hide 
19784     */
19785     /**
19786      * @hide
19787      * @method autoSize
19788      */
19789 });
19790
19791 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19792     
19793     header : {
19794         tag: 'div',
19795         cls: 'modal-header',
19796         cn: [
19797             {
19798                 tag: 'h4',
19799                 cls: 'modal-title'
19800             }
19801         ]
19802     },
19803     
19804     body : {
19805         tag: 'div',
19806         cls: 'modal-body',
19807         cn: [
19808             {
19809                 tag: 'ul',
19810                 cls: 'list-group'
19811             }
19812         ]
19813     },
19814     
19815     listItemRadio : {
19816         tag: 'li',
19817         cls: 'list-group-item',
19818         cn: [
19819             {
19820                 tag: 'span',
19821                 cls: 'roo-combobox-list-group-item-value'
19822             },
19823             {
19824                 tag: 'div',
19825                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19826                 cn: [
19827                     {
19828                         tag: 'input',
19829                         type: 'radio'
19830                     },
19831                     {
19832                         tag: 'label'
19833                     }
19834                 ]
19835             }
19836         ]
19837     },
19838     
19839     listItemCheckbox : {
19840         tag: 'li',
19841         cls: 'list-group-item',
19842         cn: [
19843             {
19844                 tag: 'span',
19845                 cls: 'roo-combobox-list-group-item-value'
19846             },
19847             {
19848                 tag: 'div',
19849                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19850                 cn: [
19851                     {
19852                         tag: 'input',
19853                         type: 'checkbox'
19854                     },
19855                     {
19856                         tag: 'label'
19857                     }
19858                 ]
19859             }
19860         ]
19861     },
19862     
19863     emptyResult : {
19864         tag: 'div',
19865         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19866     },
19867     
19868     footer : {
19869         tag: 'div',
19870         cls: 'modal-footer',
19871         cn: [
19872             {
19873                 tag: 'div',
19874                 cls: 'row',
19875                 cn: [
19876                     {
19877                         tag: 'div',
19878                         cls: 'col-xs-6 text-left',
19879                         cn: {
19880                             tag: 'button',
19881                             cls: 'btn btn-danger roo-touch-view-cancel',
19882                             html: 'Cancel'
19883                         }
19884                     },
19885                     {
19886                         tag: 'div',
19887                         cls: 'col-xs-6 text-right',
19888                         cn: {
19889                             tag: 'button',
19890                             cls: 'btn btn-success roo-touch-view-ok',
19891                             html: 'OK'
19892                         }
19893                     }
19894                 ]
19895             }
19896         ]
19897         
19898     }
19899 });
19900
19901 Roo.apply(Roo.bootstrap.form.ComboBox,  {
19902     
19903     touchViewTemplate : {
19904         tag: 'div',
19905         cls: 'modal fade roo-combobox-touch-view',
19906         cn: [
19907             {
19908                 tag: 'div',
19909                 cls: 'modal-dialog',
19910                 style : 'position:fixed', // we have to fix position....
19911                 cn: [
19912                     {
19913                         tag: 'div',
19914                         cls: 'modal-content',
19915                         cn: [
19916                             Roo.bootstrap.form.ComboBox.header,
19917                             Roo.bootstrap.form.ComboBox.body,
19918                             Roo.bootstrap.form.ComboBox.footer
19919                         ]
19920                     }
19921                 ]
19922             }
19923         ]
19924     }
19925 });/*
19926  * Based on:
19927  * Ext JS Library 1.1.1
19928  * Copyright(c) 2006-2007, Ext JS, LLC.
19929  *
19930  * Originally Released Under LGPL - original licence link has changed is not relivant.
19931  *
19932  * Fork - LGPL
19933  * <script type="text/javascript">
19934  */
19935
19936 /**
19937  * @class Roo.View
19938  * @extends Roo.util.Observable
19939  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19940  * This class also supports single and multi selection modes. <br>
19941  * Create a data model bound view:
19942  <pre><code>
19943  var store = new Roo.data.Store(...);
19944
19945  var view = new Roo.View({
19946     el : "my-element",
19947     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19948  
19949     singleSelect: true,
19950     selectedClass: "ydataview-selected",
19951     store: store
19952  });
19953
19954  // listen for node click?
19955  view.on("click", function(vw, index, node, e){
19956  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19957  });
19958
19959  // load XML data
19960  dataModel.load("foobar.xml");
19961  </code></pre>
19962  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19963  * <br><br>
19964  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19965  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19966  * 
19967  * Note: old style constructor is still suported (container, template, config)
19968  * 
19969  * @constructor
19970  * Create a new View
19971  * @param {Object} config The config object
19972  * 
19973  */
19974 Roo.View = function(config, depreciated_tpl, depreciated_config){
19975     
19976     this.parent = false;
19977     
19978     if (typeof(depreciated_tpl) == 'undefined') {
19979         // new way.. - universal constructor.
19980         Roo.apply(this, config);
19981         this.el  = Roo.get(this.el);
19982     } else {
19983         // old format..
19984         this.el  = Roo.get(config);
19985         this.tpl = depreciated_tpl;
19986         Roo.apply(this, depreciated_config);
19987     }
19988     this.wrapEl  = this.el.wrap().wrap();
19989     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19990     
19991     
19992     if(typeof(this.tpl) == "string"){
19993         this.tpl = new Roo.Template(this.tpl);
19994     } else {
19995         // support xtype ctors..
19996         this.tpl = new Roo.factory(this.tpl, Roo);
19997     }
19998     
19999     
20000     this.tpl.compile();
20001     
20002     /** @private */
20003     this.addEvents({
20004         /**
20005          * @event beforeclick
20006          * Fires before a click is processed. Returns false to cancel the default action.
20007          * @param {Roo.View} this
20008          * @param {Number} index The index of the target node
20009          * @param {HTMLElement} node The target node
20010          * @param {Roo.EventObject} e The raw event object
20011          */
20012             "beforeclick" : true,
20013         /**
20014          * @event click
20015          * Fires when a template node is clicked.
20016          * @param {Roo.View} this
20017          * @param {Number} index The index of the target node
20018          * @param {HTMLElement} node The target node
20019          * @param {Roo.EventObject} e The raw event object
20020          */
20021             "click" : true,
20022         /**
20023          * @event dblclick
20024          * Fires when a template node is double clicked.
20025          * @param {Roo.View} this
20026          * @param {Number} index The index of the target node
20027          * @param {HTMLElement} node The target node
20028          * @param {Roo.EventObject} e The raw event object
20029          */
20030             "dblclick" : true,
20031         /**
20032          * @event contextmenu
20033          * Fires when a template node is right clicked.
20034          * @param {Roo.View} this
20035          * @param {Number} index The index of the target node
20036          * @param {HTMLElement} node The target node
20037          * @param {Roo.EventObject} e The raw event object
20038          */
20039             "contextmenu" : true,
20040         /**
20041          * @event selectionchange
20042          * Fires when the selected nodes change.
20043          * @param {Roo.View} this
20044          * @param {Array} selections Array of the selected nodes
20045          */
20046             "selectionchange" : true,
20047     
20048         /**
20049          * @event beforeselect
20050          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20051          * @param {Roo.View} this
20052          * @param {HTMLElement} node The node to be selected
20053          * @param {Array} selections Array of currently selected nodes
20054          */
20055             "beforeselect" : true,
20056         /**
20057          * @event preparedata
20058          * Fires on every row to render, to allow you to change the data.
20059          * @param {Roo.View} this
20060          * @param {Object} data to be rendered (change this)
20061          */
20062           "preparedata" : true
20063           
20064           
20065         });
20066
20067
20068
20069     this.el.on({
20070         "click": this.onClick,
20071         "dblclick": this.onDblClick,
20072         "contextmenu": this.onContextMenu,
20073         scope:this
20074     });
20075
20076     this.selections = [];
20077     this.nodes = [];
20078     this.cmp = new Roo.CompositeElementLite([]);
20079     if(this.store){
20080         this.store = Roo.factory(this.store, Roo.data);
20081         this.setStore(this.store, true);
20082     }
20083     
20084     if ( this.footer && this.footer.xtype) {
20085            
20086          var fctr = this.wrapEl.appendChild(document.createElement("div"));
20087         
20088         this.footer.dataSource = this.store;
20089         this.footer.container = fctr;
20090         this.footer = Roo.factory(this.footer, Roo);
20091         fctr.insertFirst(this.el);
20092         
20093         // this is a bit insane - as the paging toolbar seems to detach the el..
20094 //        dom.parentNode.parentNode.parentNode
20095          // they get detached?
20096     }
20097     
20098     
20099     Roo.View.superclass.constructor.call(this);
20100     
20101     
20102 };
20103
20104 Roo.extend(Roo.View, Roo.util.Observable, {
20105     
20106      /**
20107      * @cfg {Roo.data.Store} store Data store to load data from.
20108      */
20109     store : false,
20110     
20111     /**
20112      * @cfg {String|Roo.Element} el The container element.
20113      */
20114     el : '',
20115     
20116     /**
20117      * @cfg {String|Roo.Template} tpl The template used by this View 
20118      */
20119     tpl : false,
20120     /**
20121      * @cfg {String} dataName the named area of the template to use as the data area
20122      *                          Works with domtemplates roo-name="name"
20123      */
20124     dataName: false,
20125     /**
20126      * @cfg {String} selectedClass The css class to add to selected nodes
20127      */
20128     selectedClass : "x-view-selected",
20129      /**
20130      * @cfg {String} emptyText The empty text to show when nothing is loaded.
20131      */
20132     emptyText : "",
20133     
20134     /**
20135      * @cfg {String} text to display on mask (default Loading)
20136      */
20137     mask : false,
20138     /**
20139      * @cfg {Boolean} multiSelect Allow multiple selection
20140      */
20141     multiSelect : false,
20142     /**
20143      * @cfg {Boolean} singleSelect Allow single selection
20144      */
20145     singleSelect:  false,
20146     
20147     /**
20148      * @cfg {Boolean} toggleSelect - selecting 
20149      */
20150     toggleSelect : false,
20151     
20152     /**
20153      * @cfg {Boolean} tickable - selecting 
20154      */
20155     tickable : false,
20156     
20157     /**
20158      * Returns the element this view is bound to.
20159      * @return {Roo.Element}
20160      */
20161     getEl : function(){
20162         return this.wrapEl;
20163     },
20164     
20165     
20166
20167     /**
20168      * Refreshes the view. - called by datachanged on the store. - do not call directly.
20169      */
20170     refresh : function(){
20171         //Roo.log('refresh');
20172         var t = this.tpl;
20173         
20174         // if we are using something like 'domtemplate', then
20175         // the what gets used is:
20176         // t.applySubtemplate(NAME, data, wrapping data..)
20177         // the outer template then get' applied with
20178         //     the store 'extra data'
20179         // and the body get's added to the
20180         //      roo-name="data" node?
20181         //      <span class='roo-tpl-{name}'></span> ?????
20182         
20183         
20184         
20185         this.clearSelections();
20186         this.el.update("");
20187         var html = [];
20188         var records = this.store.getRange();
20189         if(records.length < 1) {
20190             
20191             // is this valid??  = should it render a template??
20192             
20193             this.el.update(this.emptyText);
20194             return;
20195         }
20196         var el = this.el;
20197         if (this.dataName) {
20198             this.el.update(t.apply(this.store.meta)); //????
20199             el = this.el.child('.roo-tpl-' + this.dataName);
20200         }
20201         
20202         for(var i = 0, len = records.length; i < len; i++){
20203             var data = this.prepareData(records[i].data, i, records[i]);
20204             this.fireEvent("preparedata", this, data, i, records[i]);
20205             
20206             var d = Roo.apply({}, data);
20207             
20208             if(this.tickable){
20209                 Roo.apply(d, {'roo-id' : Roo.id()});
20210                 
20211                 var _this = this;
20212             
20213                 Roo.each(this.parent.item, function(item){
20214                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20215                         return;
20216                     }
20217                     Roo.apply(d, {'roo-data-checked' : 'checked'});
20218                 });
20219             }
20220             
20221             html[html.length] = Roo.util.Format.trim(
20222                 this.dataName ?
20223                     t.applySubtemplate(this.dataName, d, this.store.meta) :
20224                     t.apply(d)
20225             );
20226         }
20227         
20228         
20229         
20230         el.update(html.join(""));
20231         this.nodes = el.dom.childNodes;
20232         this.updateIndexes(0);
20233     },
20234     
20235
20236     /**
20237      * Function to override to reformat the data that is sent to
20238      * the template for each node.
20239      * DEPRICATED - use the preparedata event handler.
20240      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20241      * a JSON object for an UpdateManager bound view).
20242      */
20243     prepareData : function(data, index, record)
20244     {
20245         this.fireEvent("preparedata", this, data, index, record);
20246         return data;
20247     },
20248
20249     onUpdate : function(ds, record){
20250         // Roo.log('on update');   
20251         this.clearSelections();
20252         var index = this.store.indexOf(record);
20253         var n = this.nodes[index];
20254         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20255         n.parentNode.removeChild(n);
20256         this.updateIndexes(index, index);
20257     },
20258
20259     
20260     
20261 // --------- FIXME     
20262     onAdd : function(ds, records, index)
20263     {
20264         //Roo.log(['on Add', ds, records, index] );        
20265         this.clearSelections();
20266         if(this.nodes.length == 0){
20267             this.refresh();
20268             return;
20269         }
20270         var n = this.nodes[index];
20271         for(var i = 0, len = records.length; i < len; i++){
20272             var d = this.prepareData(records[i].data, i, records[i]);
20273             if(n){
20274                 this.tpl.insertBefore(n, d);
20275             }else{
20276                 
20277                 this.tpl.append(this.el, d);
20278             }
20279         }
20280         this.updateIndexes(index);
20281     },
20282
20283     onRemove : function(ds, record, index){
20284        // Roo.log('onRemove');
20285         this.clearSelections();
20286         var el = this.dataName  ?
20287             this.el.child('.roo-tpl-' + this.dataName) :
20288             this.el; 
20289         
20290         el.dom.removeChild(this.nodes[index]);
20291         this.updateIndexes(index);
20292     },
20293
20294     /**
20295      * Refresh an individual node.
20296      * @param {Number} index
20297      */
20298     refreshNode : function(index){
20299         this.onUpdate(this.store, this.store.getAt(index));
20300     },
20301
20302     updateIndexes : function(startIndex, endIndex){
20303         var ns = this.nodes;
20304         startIndex = startIndex || 0;
20305         endIndex = endIndex || ns.length - 1;
20306         for(var i = startIndex; i <= endIndex; i++){
20307             ns[i].nodeIndex = i;
20308         }
20309     },
20310
20311     /**
20312      * Changes the data store this view uses and refresh the view.
20313      * @param {Store} store
20314      */
20315     setStore : function(store, initial){
20316         if(!initial && this.store){
20317             this.store.un("datachanged", this.refresh);
20318             this.store.un("add", this.onAdd);
20319             this.store.un("remove", this.onRemove);
20320             this.store.un("update", this.onUpdate);
20321             this.store.un("clear", this.refresh);
20322             this.store.un("beforeload", this.onBeforeLoad);
20323             this.store.un("load", this.onLoad);
20324             this.store.un("loadexception", this.onLoad);
20325         }
20326         if(store){
20327           
20328             store.on("datachanged", this.refresh, this);
20329             store.on("add", this.onAdd, this);
20330             store.on("remove", this.onRemove, this);
20331             store.on("update", this.onUpdate, this);
20332             store.on("clear", this.refresh, this);
20333             store.on("beforeload", this.onBeforeLoad, this);
20334             store.on("load", this.onLoad, this);
20335             store.on("loadexception", this.onLoad, this);
20336         }
20337         
20338         if(store){
20339             this.refresh();
20340         }
20341     },
20342     /**
20343      * onbeforeLoad - masks the loading area.
20344      *
20345      */
20346     onBeforeLoad : function(store,opts)
20347     {
20348          //Roo.log('onBeforeLoad');   
20349         if (!opts.add) {
20350             this.el.update("");
20351         }
20352         this.el.mask(this.mask ? this.mask : "Loading" ); 
20353     },
20354     onLoad : function ()
20355     {
20356         this.el.unmask();
20357     },
20358     
20359
20360     /**
20361      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20362      * @param {HTMLElement} node
20363      * @return {HTMLElement} The template node
20364      */
20365     findItemFromChild : function(node){
20366         var el = this.dataName  ?
20367             this.el.child('.roo-tpl-' + this.dataName,true) :
20368             this.el.dom; 
20369         
20370         if(!node || node.parentNode == el){
20371                     return node;
20372             }
20373             var p = node.parentNode;
20374             while(p && p != el){
20375             if(p.parentNode == el){
20376                 return p;
20377             }
20378             p = p.parentNode;
20379         }
20380             return null;
20381     },
20382
20383     /** @ignore */
20384     onClick : function(e){
20385         var item = this.findItemFromChild(e.getTarget());
20386         if(item){
20387             var index = this.indexOf(item);
20388             if(this.onItemClick(item, index, e) !== false){
20389                 this.fireEvent("click", this, index, item, e);
20390             }
20391         }else{
20392             this.clearSelections();
20393         }
20394     },
20395
20396     /** @ignore */
20397     onContextMenu : function(e){
20398         var item = this.findItemFromChild(e.getTarget());
20399         if(item){
20400             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20401         }
20402     },
20403
20404     /** @ignore */
20405     onDblClick : function(e){
20406         var item = this.findItemFromChild(e.getTarget());
20407         if(item){
20408             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20409         }
20410     },
20411
20412     onItemClick : function(item, index, e)
20413     {
20414         if(this.fireEvent("beforeclick", this, index, item, e) === false){
20415             return false;
20416         }
20417         if (this.toggleSelect) {
20418             var m = this.isSelected(item) ? 'unselect' : 'select';
20419             //Roo.log(m);
20420             var _t = this;
20421             _t[m](item, true, false);
20422             return true;
20423         }
20424         if(this.multiSelect || this.singleSelect){
20425             if(this.multiSelect && e.shiftKey && this.lastSelection){
20426                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20427             }else{
20428                 this.select(item, this.multiSelect && e.ctrlKey);
20429                 this.lastSelection = item;
20430             }
20431             
20432             if(!this.tickable){
20433                 e.preventDefault();
20434             }
20435             
20436         }
20437         return true;
20438     },
20439
20440     /**
20441      * Get the number of selected nodes.
20442      * @return {Number}
20443      */
20444     getSelectionCount : function(){
20445         return this.selections.length;
20446     },
20447
20448     /**
20449      * Get the currently selected nodes.
20450      * @return {Array} An array of HTMLElements
20451      */
20452     getSelectedNodes : function(){
20453         return this.selections;
20454     },
20455
20456     /**
20457      * Get the indexes of the selected nodes.
20458      * @return {Array}
20459      */
20460     getSelectedIndexes : function(){
20461         var indexes = [], s = this.selections;
20462         for(var i = 0, len = s.length; i < len; i++){
20463             indexes.push(s[i].nodeIndex);
20464         }
20465         return indexes;
20466     },
20467
20468     /**
20469      * Clear all selections
20470      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20471      */
20472     clearSelections : function(suppressEvent){
20473         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20474             this.cmp.elements = this.selections;
20475             this.cmp.removeClass(this.selectedClass);
20476             this.selections = [];
20477             if(!suppressEvent){
20478                 this.fireEvent("selectionchange", this, this.selections);
20479             }
20480         }
20481     },
20482
20483     /**
20484      * Returns true if the passed node is selected
20485      * @param {HTMLElement/Number} node The node or node index
20486      * @return {Boolean}
20487      */
20488     isSelected : function(node){
20489         var s = this.selections;
20490         if(s.length < 1){
20491             return false;
20492         }
20493         node = this.getNode(node);
20494         return s.indexOf(node) !== -1;
20495     },
20496
20497     /**
20498      * Selects nodes.
20499      * @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
20500      * @param {Boolean} keepExisting (optional) true to keep existing selections
20501      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20502      */
20503     select : function(nodeInfo, keepExisting, suppressEvent){
20504         if(nodeInfo instanceof Array){
20505             if(!keepExisting){
20506                 this.clearSelections(true);
20507             }
20508             for(var i = 0, len = nodeInfo.length; i < len; i++){
20509                 this.select(nodeInfo[i], true, true);
20510             }
20511             return;
20512         } 
20513         var node = this.getNode(nodeInfo);
20514         if(!node || this.isSelected(node)){
20515             return; // already selected.
20516         }
20517         if(!keepExisting){
20518             this.clearSelections(true);
20519         }
20520         
20521         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20522             Roo.fly(node).addClass(this.selectedClass);
20523             this.selections.push(node);
20524             if(!suppressEvent){
20525                 this.fireEvent("selectionchange", this, this.selections);
20526             }
20527         }
20528         
20529         
20530     },
20531       /**
20532      * Unselects nodes.
20533      * @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
20534      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20536      */
20537     unselect : function(nodeInfo, keepExisting, suppressEvent)
20538     {
20539         if(nodeInfo instanceof Array){
20540             Roo.each(this.selections, function(s) {
20541                 this.unselect(s, nodeInfo);
20542             }, this);
20543             return;
20544         }
20545         var node = this.getNode(nodeInfo);
20546         if(!node || !this.isSelected(node)){
20547             //Roo.log("not selected");
20548             return; // not selected.
20549         }
20550         // fireevent???
20551         var ns = [];
20552         Roo.each(this.selections, function(s) {
20553             if (s == node ) {
20554                 Roo.fly(node).removeClass(this.selectedClass);
20555
20556                 return;
20557             }
20558             ns.push(s);
20559         },this);
20560         
20561         this.selections= ns;
20562         this.fireEvent("selectionchange", this, this.selections);
20563     },
20564
20565     /**
20566      * Gets a template node.
20567      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20568      * @return {HTMLElement} The node or null if it wasn't found
20569      */
20570     getNode : function(nodeInfo){
20571         if(typeof nodeInfo == "string"){
20572             return document.getElementById(nodeInfo);
20573         }else if(typeof nodeInfo == "number"){
20574             return this.nodes[nodeInfo];
20575         }
20576         return nodeInfo;
20577     },
20578
20579     /**
20580      * Gets a range template nodes.
20581      * @param {Number} startIndex
20582      * @param {Number} endIndex
20583      * @return {Array} An array of nodes
20584      */
20585     getNodes : function(start, end){
20586         var ns = this.nodes;
20587         start = start || 0;
20588         end = typeof end == "undefined" ? ns.length - 1 : end;
20589         var nodes = [];
20590         if(start <= end){
20591             for(var i = start; i <= end; i++){
20592                 nodes.push(ns[i]);
20593             }
20594         } else{
20595             for(var i = start; i >= end; i--){
20596                 nodes.push(ns[i]);
20597             }
20598         }
20599         return nodes;
20600     },
20601
20602     /**
20603      * Finds the index of the passed node
20604      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20605      * @return {Number} The index of the node or -1
20606      */
20607     indexOf : function(node){
20608         node = this.getNode(node);
20609         if(typeof node.nodeIndex == "number"){
20610             return node.nodeIndex;
20611         }
20612         var ns = this.nodes;
20613         for(var i = 0, len = ns.length; i < len; i++){
20614             if(ns[i] == node){
20615                 return i;
20616             }
20617         }
20618         return -1;
20619     }
20620 });
20621 /*
20622  * - LGPL
20623  *
20624  * based on jquery fullcalendar
20625  * 
20626  */
20627
20628 Roo.bootstrap = Roo.bootstrap || {};
20629 /**
20630  * @class Roo.bootstrap.Calendar
20631  * @extends Roo.bootstrap.Component
20632  * Bootstrap Calendar class
20633  * @cfg {Boolean} loadMask (true|false) default false
20634  * @cfg {Object} header generate the user specific header of the calendar, default false
20635
20636  * @constructor
20637  * Create a new Container
20638  * @param {Object} config The config object
20639  */
20640
20641
20642
20643 Roo.bootstrap.Calendar = function(config){
20644     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20645      this.addEvents({
20646         /**
20647              * @event select
20648              * Fires when a date is selected
20649              * @param {DatePicker} this
20650              * @param {Date} date The selected date
20651              */
20652         'select': true,
20653         /**
20654              * @event monthchange
20655              * Fires when the displayed month changes 
20656              * @param {DatePicker} this
20657              * @param {Date} date The selected month
20658              */
20659         'monthchange': true,
20660         /**
20661              * @event evententer
20662              * Fires when mouse over an event
20663              * @param {Calendar} this
20664              * @param {event} Event
20665              */
20666         'evententer': true,
20667         /**
20668              * @event eventleave
20669              * Fires when the mouse leaves an
20670              * @param {Calendar} this
20671              * @param {event}
20672              */
20673         'eventleave': true,
20674         /**
20675              * @event eventclick
20676              * Fires when the mouse click an
20677              * @param {Calendar} this
20678              * @param {event}
20679              */
20680         'eventclick': true
20681         
20682     });
20683
20684 };
20685
20686 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20687     
20688           /**
20689      * @cfg {Roo.data.Store} store
20690      * The data source for the calendar
20691      */
20692         store : false,
20693      /**
20694      * @cfg {Number} startDay
20695      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20696      */
20697     startDay : 0,
20698     
20699     loadMask : false,
20700     
20701     header : false,
20702       
20703     getAutoCreate : function(){
20704         
20705         
20706         var fc_button = function(name, corner, style, content ) {
20707             return Roo.apply({},{
20708                 tag : 'span',
20709                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20710                          (corner.length ?
20711                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20712                             ''
20713                         ),
20714                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20715                 unselectable: 'on'
20716             });
20717         };
20718         
20719         var header = {};
20720         
20721         if(!this.header){
20722             header = {
20723                 tag : 'table',
20724                 cls : 'fc-header',
20725                 style : 'width:100%',
20726                 cn : [
20727                     {
20728                         tag: 'tr',
20729                         cn : [
20730                             {
20731                                 tag : 'td',
20732                                 cls : 'fc-header-left',
20733                                 cn : [
20734                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20735                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20736                                     { tag: 'span', cls: 'fc-header-space' },
20737                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20738
20739
20740                                 ]
20741                             },
20742
20743                             {
20744                                 tag : 'td',
20745                                 cls : 'fc-header-center',
20746                                 cn : [
20747                                     {
20748                                         tag: 'span',
20749                                         cls: 'fc-header-title',
20750                                         cn : {
20751                                             tag: 'H2',
20752                                             html : 'month / year'
20753                                         }
20754                                     }
20755
20756                                 ]
20757                             },
20758                             {
20759                                 tag : 'td',
20760                                 cls : 'fc-header-right',
20761                                 cn : [
20762                               /*      fc_button('month', 'left', '', 'month' ),
20763                                     fc_button('week', '', '', 'week' ),
20764                                     fc_button('day', 'right', '', 'day' )
20765                                 */    
20766
20767                                 ]
20768                             }
20769
20770                         ]
20771                     }
20772                 ]
20773             };
20774         }
20775         
20776         header = this.header;
20777         
20778        
20779         var cal_heads = function() {
20780             var ret = [];
20781             // fixme - handle this.
20782             
20783             for (var i =0; i < Date.dayNames.length; i++) {
20784                 var d = Date.dayNames[i];
20785                 ret.push({
20786                     tag: 'th',
20787                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20788                     html : d.substring(0,3)
20789                 });
20790                 
20791             }
20792             ret[0].cls += ' fc-first';
20793             ret[6].cls += ' fc-last';
20794             return ret;
20795         };
20796         var cal_cell = function(n) {
20797             return  {
20798                 tag: 'td',
20799                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20800                 cn : [
20801                     {
20802                         cn : [
20803                             {
20804                                 cls: 'fc-day-number',
20805                                 html: 'D'
20806                             },
20807                             {
20808                                 cls: 'fc-day-content',
20809                              
20810                                 cn : [
20811                                      {
20812                                         style: 'position: relative;' // height: 17px;
20813                                     }
20814                                 ]
20815                             }
20816                             
20817                             
20818                         ]
20819                     }
20820                 ]
20821                 
20822             }
20823         };
20824         var cal_rows = function() {
20825             
20826             var ret = [];
20827             for (var r = 0; r < 6; r++) {
20828                 var row= {
20829                     tag : 'tr',
20830                     cls : 'fc-week',
20831                     cn : []
20832                 };
20833                 
20834                 for (var i =0; i < Date.dayNames.length; i++) {
20835                     var d = Date.dayNames[i];
20836                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20837
20838                 }
20839                 row.cn[0].cls+=' fc-first';
20840                 row.cn[0].cn[0].style = 'min-height:90px';
20841                 row.cn[6].cls+=' fc-last';
20842                 ret.push(row);
20843                 
20844             }
20845             ret[0].cls += ' fc-first';
20846             ret[4].cls += ' fc-prev-last';
20847             ret[5].cls += ' fc-last';
20848             return ret;
20849             
20850         };
20851         
20852         var cal_table = {
20853             tag: 'table',
20854             cls: 'fc-border-separate',
20855             style : 'width:100%',
20856             cellspacing  : 0,
20857             cn : [
20858                 { 
20859                     tag: 'thead',
20860                     cn : [
20861                         { 
20862                             tag: 'tr',
20863                             cls : 'fc-first fc-last',
20864                             cn : cal_heads()
20865                         }
20866                     ]
20867                 },
20868                 { 
20869                     tag: 'tbody',
20870                     cn : cal_rows()
20871                 }
20872                   
20873             ]
20874         };
20875          
20876          var cfg = {
20877             cls : 'fc fc-ltr',
20878             cn : [
20879                 header,
20880                 {
20881                     cls : 'fc-content',
20882                     style : "position: relative;",
20883                     cn : [
20884                         {
20885                             cls : 'fc-view fc-view-month fc-grid',
20886                             style : 'position: relative',
20887                             unselectable : 'on',
20888                             cn : [
20889                                 {
20890                                     cls : 'fc-event-container',
20891                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20892                                 },
20893                                 cal_table
20894                             ]
20895                         }
20896                     ]
20897     
20898                 }
20899            ] 
20900             
20901         };
20902         
20903          
20904         
20905         return cfg;
20906     },
20907     
20908     
20909     initEvents : function()
20910     {
20911         if(!this.store){
20912             throw "can not find store for calendar";
20913         }
20914         
20915         var mark = {
20916             tag: "div",
20917             cls:"x-dlg-mask",
20918             style: "text-align:center",
20919             cn: [
20920                 {
20921                     tag: "div",
20922                     style: "background-color:white;width:50%;margin:250 auto",
20923                     cn: [
20924                         {
20925                             tag: "img",
20926                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20927                         },
20928                         {
20929                             tag: "span",
20930                             html: "Loading"
20931                         }
20932                         
20933                     ]
20934                 }
20935             ]
20936         };
20937         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20938         
20939         var size = this.el.select('.fc-content', true).first().getSize();
20940         this.maskEl.setSize(size.width, size.height);
20941         this.maskEl.enableDisplayMode("block");
20942         if(!this.loadMask){
20943             this.maskEl.hide();
20944         }
20945         
20946         this.store = Roo.factory(this.store, Roo.data);
20947         this.store.on('load', this.onLoad, this);
20948         this.store.on('beforeload', this.onBeforeLoad, this);
20949         
20950         this.resize();
20951         
20952         this.cells = this.el.select('.fc-day',true);
20953         //Roo.log(this.cells);
20954         this.textNodes = this.el.query('.fc-day-number');
20955         this.cells.addClassOnOver('fc-state-hover');
20956         
20957         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20958         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20959         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20960         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20961         
20962         this.on('monthchange', this.onMonthChange, this);
20963         
20964         this.update(new Date().clearTime());
20965     },
20966     
20967     resize : function() {
20968         var sz  = this.el.getSize();
20969         
20970         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20971         this.el.select('.fc-day-content div',true).setHeight(34);
20972     },
20973     
20974     
20975     // private
20976     showPrevMonth : function(e){
20977         this.update(this.activeDate.add("mo", -1));
20978     },
20979     showToday : function(e){
20980         this.update(new Date().clearTime());
20981     },
20982     // private
20983     showNextMonth : function(e){
20984         this.update(this.activeDate.add("mo", 1));
20985     },
20986
20987     // private
20988     showPrevYear : function(){
20989         this.update(this.activeDate.add("y", -1));
20990     },
20991
20992     // private
20993     showNextYear : function(){
20994         this.update(this.activeDate.add("y", 1));
20995     },
20996
20997     
20998    // private
20999     update : function(date)
21000     {
21001         var vd = this.activeDate;
21002         this.activeDate = date;
21003 //        if(vd && this.el){
21004 //            var t = date.getTime();
21005 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21006 //                Roo.log('using add remove');
21007 //                
21008 //                this.fireEvent('monthchange', this, date);
21009 //                
21010 //                this.cells.removeClass("fc-state-highlight");
21011 //                this.cells.each(function(c){
21012 //                   if(c.dateValue == t){
21013 //                       c.addClass("fc-state-highlight");
21014 //                       setTimeout(function(){
21015 //                            try{c.dom.firstChild.focus();}catch(e){}
21016 //                       }, 50);
21017 //                       return false;
21018 //                   }
21019 //                   return true;
21020 //                });
21021 //                return;
21022 //            }
21023 //        }
21024         
21025         var days = date.getDaysInMonth();
21026         
21027         var firstOfMonth = date.getFirstDateOfMonth();
21028         var startingPos = firstOfMonth.getDay()-this.startDay;
21029         
21030         if(startingPos < this.startDay){
21031             startingPos += 7;
21032         }
21033         
21034         var pm = date.add(Date.MONTH, -1);
21035         var prevStart = pm.getDaysInMonth()-startingPos;
21036 //        
21037         this.cells = this.el.select('.fc-day',true);
21038         this.textNodes = this.el.query('.fc-day-number');
21039         this.cells.addClassOnOver('fc-state-hover');
21040         
21041         var cells = this.cells.elements;
21042         var textEls = this.textNodes;
21043         
21044         Roo.each(cells, function(cell){
21045             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21046         });
21047         
21048         days += startingPos;
21049
21050         // convert everything to numbers so it's fast
21051         var day = 86400000;
21052         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21053         //Roo.log(d);
21054         //Roo.log(pm);
21055         //Roo.log(prevStart);
21056         
21057         var today = new Date().clearTime().getTime();
21058         var sel = date.clearTime().getTime();
21059         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21060         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21061         var ddMatch = this.disabledDatesRE;
21062         var ddText = this.disabledDatesText;
21063         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21064         var ddaysText = this.disabledDaysText;
21065         var format = this.format;
21066         
21067         var setCellClass = function(cal, cell){
21068             cell.row = 0;
21069             cell.events = [];
21070             cell.more = [];
21071             //Roo.log('set Cell Class');
21072             cell.title = "";
21073             var t = d.getTime();
21074             
21075             //Roo.log(d);
21076             
21077             cell.dateValue = t;
21078             if(t == today){
21079                 cell.className += " fc-today";
21080                 cell.className += " fc-state-highlight";
21081                 cell.title = cal.todayText;
21082             }
21083             if(t == sel){
21084                 // disable highlight in other month..
21085                 //cell.className += " fc-state-highlight";
21086                 
21087             }
21088             // disabling
21089             if(t < min) {
21090                 cell.className = " fc-state-disabled";
21091                 cell.title = cal.minText;
21092                 return;
21093             }
21094             if(t > max) {
21095                 cell.className = " fc-state-disabled";
21096                 cell.title = cal.maxText;
21097                 return;
21098             }
21099             if(ddays){
21100                 if(ddays.indexOf(d.getDay()) != -1){
21101                     cell.title = ddaysText;
21102                     cell.className = " fc-state-disabled";
21103                 }
21104             }
21105             if(ddMatch && format){
21106                 var fvalue = d.dateFormat(format);
21107                 if(ddMatch.test(fvalue)){
21108                     cell.title = ddText.replace("%0", fvalue);
21109                     cell.className = " fc-state-disabled";
21110                 }
21111             }
21112             
21113             if (!cell.initialClassName) {
21114                 cell.initialClassName = cell.dom.className;
21115             }
21116             
21117             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
21118         };
21119
21120         var i = 0;
21121         
21122         for(; i < startingPos; i++) {
21123             textEls[i].innerHTML = (++prevStart);
21124             d.setDate(d.getDate()+1);
21125             
21126             cells[i].className = "fc-past fc-other-month";
21127             setCellClass(this, cells[i]);
21128         }
21129         
21130         var intDay = 0;
21131         
21132         for(; i < days; i++){
21133             intDay = i - startingPos + 1;
21134             textEls[i].innerHTML = (intDay);
21135             d.setDate(d.getDate()+1);
21136             
21137             cells[i].className = ''; // "x-date-active";
21138             setCellClass(this, cells[i]);
21139         }
21140         var extraDays = 0;
21141         
21142         for(; i < 42; i++) {
21143             textEls[i].innerHTML = (++extraDays);
21144             d.setDate(d.getDate()+1);
21145             
21146             cells[i].className = "fc-future fc-other-month";
21147             setCellClass(this, cells[i]);
21148         }
21149         
21150         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21151         
21152         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21153         
21154         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21155         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21156         
21157         if(totalRows != 6){
21158             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21159             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21160         }
21161         
21162         this.fireEvent('monthchange', this, date);
21163         
21164         
21165         /*
21166         if(!this.internalRender){
21167             var main = this.el.dom.firstChild;
21168             var w = main.offsetWidth;
21169             this.el.setWidth(w + this.el.getBorderWidth("lr"));
21170             Roo.fly(main).setWidth(w);
21171             this.internalRender = true;
21172             // opera does not respect the auto grow header center column
21173             // then, after it gets a width opera refuses to recalculate
21174             // without a second pass
21175             if(Roo.isOpera && !this.secondPass){
21176                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21177                 this.secondPass = true;
21178                 this.update.defer(10, this, [date]);
21179             }
21180         }
21181         */
21182         
21183     },
21184     
21185     findCell : function(dt) {
21186         dt = dt.clearTime().getTime();
21187         var ret = false;
21188         this.cells.each(function(c){
21189             //Roo.log("check " +c.dateValue + '?=' + dt);
21190             if(c.dateValue == dt){
21191                 ret = c;
21192                 return false;
21193             }
21194             return true;
21195         });
21196         
21197         return ret;
21198     },
21199     
21200     findCells : function(ev) {
21201         var s = ev.start.clone().clearTime().getTime();
21202        // Roo.log(s);
21203         var e= ev.end.clone().clearTime().getTime();
21204        // Roo.log(e);
21205         var ret = [];
21206         this.cells.each(function(c){
21207              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21208             
21209             if(c.dateValue > e){
21210                 return ;
21211             }
21212             if(c.dateValue < s){
21213                 return ;
21214             }
21215             ret.push(c);
21216         });
21217         
21218         return ret;    
21219     },
21220     
21221 //    findBestRow: function(cells)
21222 //    {
21223 //        var ret = 0;
21224 //        
21225 //        for (var i =0 ; i < cells.length;i++) {
21226 //            ret  = Math.max(cells[i].rows || 0,ret);
21227 //        }
21228 //        return ret;
21229 //        
21230 //    },
21231     
21232     
21233     addItem : function(ev)
21234     {
21235         // look for vertical location slot in
21236         var cells = this.findCells(ev);
21237         
21238 //        ev.row = this.findBestRow(cells);
21239         
21240         // work out the location.
21241         
21242         var crow = false;
21243         var rows = [];
21244         for(var i =0; i < cells.length; i++) {
21245             
21246             cells[i].row = cells[0].row;
21247             
21248             if(i == 0){
21249                 cells[i].row = cells[i].row + 1;
21250             }
21251             
21252             if (!crow) {
21253                 crow = {
21254                     start : cells[i],
21255                     end :  cells[i]
21256                 };
21257                 continue;
21258             }
21259             if (crow.start.getY() == cells[i].getY()) {
21260                 // on same row.
21261                 crow.end = cells[i];
21262                 continue;
21263             }
21264             // different row.
21265             rows.push(crow);
21266             crow = {
21267                 start: cells[i],
21268                 end : cells[i]
21269             };
21270             
21271         }
21272         
21273         rows.push(crow);
21274         ev.els = [];
21275         ev.rows = rows;
21276         ev.cells = cells;
21277         
21278         cells[0].events.push(ev);
21279         
21280         this.calevents.push(ev);
21281     },
21282     
21283     clearEvents: function() {
21284         
21285         if(!this.calevents){
21286             return;
21287         }
21288         
21289         Roo.each(this.cells.elements, function(c){
21290             c.row = 0;
21291             c.events = [];
21292             c.more = [];
21293         });
21294         
21295         Roo.each(this.calevents, function(e) {
21296             Roo.each(e.els, function(el) {
21297                 el.un('mouseenter' ,this.onEventEnter, this);
21298                 el.un('mouseleave' ,this.onEventLeave, this);
21299                 el.remove();
21300             },this);
21301         },this);
21302         
21303         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21304             e.remove();
21305         });
21306         
21307     },
21308     
21309     renderEvents: function()
21310     {   
21311         var _this = this;
21312         
21313         this.cells.each(function(c) {
21314             
21315             if(c.row < 5){
21316                 return;
21317             }
21318             
21319             var ev = c.events;
21320             
21321             var r = 4;
21322             if(c.row != c.events.length){
21323                 r = 4 - (4 - (c.row - c.events.length));
21324             }
21325             
21326             c.events = ev.slice(0, r);
21327             c.more = ev.slice(r);
21328             
21329             if(c.more.length && c.more.length == 1){
21330                 c.events.push(c.more.pop());
21331             }
21332             
21333             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21334             
21335         });
21336             
21337         this.cells.each(function(c) {
21338             
21339             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21340             
21341             
21342             for (var e = 0; e < c.events.length; e++){
21343                 var ev = c.events[e];
21344                 var rows = ev.rows;
21345                 
21346                 for(var i = 0; i < rows.length; i++) {
21347                 
21348                     // how many rows should it span..
21349
21350                     var  cfg = {
21351                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21352                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21353
21354                         unselectable : "on",
21355                         cn : [
21356                             {
21357                                 cls: 'fc-event-inner',
21358                                 cn : [
21359     //                                {
21360     //                                  tag:'span',
21361     //                                  cls: 'fc-event-time',
21362     //                                  html : cells.length > 1 ? '' : ev.time
21363     //                                },
21364                                     {
21365                                       tag:'span',
21366                                       cls: 'fc-event-title',
21367                                       html : String.format('{0}', ev.title)
21368                                     }
21369
21370
21371                                 ]
21372                             },
21373                             {
21374                                 cls: 'ui-resizable-handle ui-resizable-e',
21375                                 html : '&nbsp;&nbsp;&nbsp'
21376                             }
21377
21378                         ]
21379                     };
21380
21381                     if (i == 0) {
21382                         cfg.cls += ' fc-event-start';
21383                     }
21384                     if ((i+1) == rows.length) {
21385                         cfg.cls += ' fc-event-end';
21386                     }
21387
21388                     var ctr = _this.el.select('.fc-event-container',true).first();
21389                     var cg = ctr.createChild(cfg);
21390
21391                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21392                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21393
21394                     var r = (c.more.length) ? 1 : 0;
21395                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
21396                     cg.setWidth(ebox.right - sbox.x -2);
21397
21398                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21399                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21400                     cg.on('click', _this.onEventClick, _this, ev);
21401
21402                     ev.els.push(cg);
21403                     
21404                 }
21405                 
21406             }
21407             
21408             
21409             if(c.more.length){
21410                 var  cfg = {
21411                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21412                     style : 'position: absolute',
21413                     unselectable : "on",
21414                     cn : [
21415                         {
21416                             cls: 'fc-event-inner',
21417                             cn : [
21418                                 {
21419                                   tag:'span',
21420                                   cls: 'fc-event-title',
21421                                   html : 'More'
21422                                 }
21423
21424
21425                             ]
21426                         },
21427                         {
21428                             cls: 'ui-resizable-handle ui-resizable-e',
21429                             html : '&nbsp;&nbsp;&nbsp'
21430                         }
21431
21432                     ]
21433                 };
21434
21435                 var ctr = _this.el.select('.fc-event-container',true).first();
21436                 var cg = ctr.createChild(cfg);
21437
21438                 var sbox = c.select('.fc-day-content',true).first().getBox();
21439                 var ebox = c.select('.fc-day-content',true).first().getBox();
21440                 //Roo.log(cg);
21441                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
21442                 cg.setWidth(ebox.right - sbox.x -2);
21443
21444                 cg.on('click', _this.onMoreEventClick, _this, c.more);
21445                 
21446             }
21447             
21448         });
21449         
21450         
21451         
21452     },
21453     
21454     onEventEnter: function (e, el,event,d) {
21455         this.fireEvent('evententer', this, el, event);
21456     },
21457     
21458     onEventLeave: function (e, el,event,d) {
21459         this.fireEvent('eventleave', this, el, event);
21460     },
21461     
21462     onEventClick: function (e, el,event,d) {
21463         this.fireEvent('eventclick', this, el, event);
21464     },
21465     
21466     onMonthChange: function () {
21467         this.store.load();
21468     },
21469     
21470     onMoreEventClick: function(e, el, more)
21471     {
21472         var _this = this;
21473         
21474         this.calpopover.placement = 'right';
21475         this.calpopover.setTitle('More');
21476         
21477         this.calpopover.setContent('');
21478         
21479         var ctr = this.calpopover.el.select('.popover-content', true).first();
21480         
21481         Roo.each(more, function(m){
21482             var cfg = {
21483                 cls : 'fc-event-hori fc-event-draggable',
21484                 html : m.title
21485             };
21486             var cg = ctr.createChild(cfg);
21487             
21488             cg.on('click', _this.onEventClick, _this, m);
21489         });
21490         
21491         this.calpopover.show(el);
21492         
21493         
21494     },
21495     
21496     onLoad: function () 
21497     {   
21498         this.calevents = [];
21499         var cal = this;
21500         
21501         if(this.store.getCount() > 0){
21502             this.store.data.each(function(d){
21503                cal.addItem({
21504                     id : d.data.id,
21505                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21506                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21507                     time : d.data.start_time,
21508                     title : d.data.title,
21509                     description : d.data.description,
21510                     venue : d.data.venue
21511                 });
21512             });
21513         }
21514         
21515         this.renderEvents();
21516         
21517         if(this.calevents.length && this.loadMask){
21518             this.maskEl.hide();
21519         }
21520     },
21521     
21522     onBeforeLoad: function()
21523     {
21524         this.clearEvents();
21525         if(this.loadMask){
21526             this.maskEl.show();
21527         }
21528     }
21529 });
21530
21531  
21532  /*
21533  * - LGPL
21534  *
21535  * element
21536  * 
21537  */
21538
21539 /**
21540  * @class Roo.bootstrap.Popover
21541  * @extends Roo.bootstrap.Component
21542  * @parent none builder
21543  * @children Roo.bootstrap.Component
21544  * Bootstrap Popover class
21545  * @cfg {String} html contents of the popover   (or false to use children..)
21546  * @cfg {String} title of popover (or false to hide)
21547  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21548  * @cfg {String} trigger click || hover (or false to trigger manually)
21549  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21550  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21551  *      - if false and it has a 'parent' then it will be automatically added to that element
21552  *      - if string - Roo.get  will be called 
21553  * @cfg {Number} delay - delay before showing
21554  
21555  * @constructor
21556  * Create a new Popover
21557  * @param {Object} config The config object
21558  */
21559
21560 Roo.bootstrap.Popover = function(config){
21561     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21562     
21563     this.addEvents({
21564         // raw events
21565          /**
21566          * @event show
21567          * After the popover show
21568          * 
21569          * @param {Roo.bootstrap.Popover} this
21570          */
21571         "show" : true,
21572         /**
21573          * @event hide
21574          * After the popover hide
21575          * 
21576          * @param {Roo.bootstrap.Popover} this
21577          */
21578         "hide" : true
21579     });
21580 };
21581
21582 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21583     
21584     title: false,
21585     html: false,
21586     
21587     placement : 'right',
21588     trigger : 'hover', // hover
21589     modal : false,
21590     delay : 0,
21591     
21592     over: false,
21593     
21594     can_build_overlaid : false,
21595     
21596     maskEl : false, // the mask element
21597     headerEl : false,
21598     contentEl : false,
21599     alignEl : false, // when show is called with an element - this get's stored.
21600     
21601     getChildContainer : function()
21602     {
21603         return this.contentEl;
21604         
21605     },
21606     getPopoverHeader : function()
21607     {
21608         this.title = true; // flag not to hide it..
21609         this.headerEl.addClass('p-0');
21610         return this.headerEl
21611     },
21612     
21613     
21614     getAutoCreate : function(){
21615          
21616         var cfg = {
21617            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21618            style: 'display:block',
21619            cn : [
21620                 {
21621                     cls : 'arrow'
21622                 },
21623                 {
21624                     cls : 'popover-inner ',
21625                     cn : [
21626                         {
21627                             tag: 'h3',
21628                             cls: 'popover-title popover-header',
21629                             html : this.title === false ? '' : this.title
21630                         },
21631                         {
21632                             cls : 'popover-content popover-body '  + (this.cls || ''),
21633                             html : this.html || ''
21634                         }
21635                     ]
21636                     
21637                 }
21638            ]
21639         };
21640         
21641         return cfg;
21642     },
21643     /**
21644      * @param {string} the title
21645      */
21646     setTitle: function(str)
21647     {
21648         this.title = str;
21649         if (this.el) {
21650             this.headerEl.dom.innerHTML = str;
21651         }
21652         
21653     },
21654     /**
21655      * @param {string} the body content
21656      */
21657     setContent: function(str)
21658     {
21659         this.html = str;
21660         if (this.contentEl) {
21661             this.contentEl.dom.innerHTML = str;
21662         }
21663         
21664     },
21665     // as it get's added to the bottom of the page.
21666     onRender : function(ct, position)
21667     {
21668         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21669         
21670         
21671         
21672         if(!this.el){
21673             var cfg = Roo.apply({},  this.getAutoCreate());
21674             cfg.id = Roo.id();
21675             
21676             if (this.cls) {
21677                 cfg.cls += ' ' + this.cls;
21678             }
21679             if (this.style) {
21680                 cfg.style = this.style;
21681             }
21682             //Roo.log("adding to ");
21683             this.el = Roo.get(document.body).createChild(cfg, position);
21684 //            Roo.log(this.el);
21685         }
21686         
21687         this.contentEl = this.el.select('.popover-content',true).first();
21688         this.headerEl =  this.el.select('.popover-title',true).first();
21689         
21690         var nitems = [];
21691         if(typeof(this.items) != 'undefined'){
21692             var items = this.items;
21693             delete this.items;
21694
21695             for(var i =0;i < items.length;i++) {
21696                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21697             }
21698         }
21699
21700         this.items = nitems;
21701         
21702         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21703         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21704         
21705         
21706         
21707         this.initEvents();
21708     },
21709     
21710     resizeMask : function()
21711     {
21712         this.maskEl.setSize(
21713             Roo.lib.Dom.getViewWidth(true),
21714             Roo.lib.Dom.getViewHeight(true)
21715         );
21716     },
21717     
21718     initEvents : function()
21719     {
21720         
21721         if (!this.modal) { 
21722             Roo.bootstrap.Popover.register(this);
21723         }
21724          
21725         this.arrowEl = this.el.select('.arrow',true).first();
21726         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21727         this.el.enableDisplayMode('block');
21728         this.el.hide();
21729  
21730         
21731         if (this.over === false && !this.parent()) {
21732             return; 
21733         }
21734         if (this.triggers === false) {
21735             return;
21736         }
21737          
21738         // support parent
21739         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21740         var triggers = this.trigger ? this.trigger.split(' ') : [];
21741         Roo.each(triggers, function(trigger) {
21742         
21743             if (trigger == 'click') {
21744                 on_el.on('click', this.toggle, this);
21745             } else if (trigger != 'manual') {
21746                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21747                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21748       
21749                 on_el.on(eventIn  ,this.enter, this);
21750                 on_el.on(eventOut, this.leave, this);
21751             }
21752         }, this);
21753     },
21754     
21755     
21756     // private
21757     timeout : null,
21758     hoverState : null,
21759     
21760     toggle : function () {
21761         this.hoverState == 'in' ? this.leave() : this.enter();
21762     },
21763     
21764     enter : function () {
21765         
21766         clearTimeout(this.timeout);
21767     
21768         this.hoverState = 'in';
21769     
21770         if (!this.delay || !this.delay.show) {
21771             this.show();
21772             return;
21773         }
21774         var _t = this;
21775         this.timeout = setTimeout(function () {
21776             if (_t.hoverState == 'in') {
21777                 _t.show();
21778             }
21779         }, this.delay.show)
21780     },
21781     
21782     leave : function() {
21783         clearTimeout(this.timeout);
21784     
21785         this.hoverState = 'out';
21786     
21787         if (!this.delay || !this.delay.hide) {
21788             this.hide();
21789             return;
21790         }
21791         var _t = this;
21792         this.timeout = setTimeout(function () {
21793             if (_t.hoverState == 'out') {
21794                 _t.hide();
21795             }
21796         }, this.delay.hide)
21797     },
21798     
21799     /**
21800      * update the position of the dialog
21801      * normally this is needed if the popover get's bigger - due to a Table reload etc..
21802      * 
21803      *
21804      */
21805     
21806     doAlign : function()
21807     {
21808         
21809         if (this.alignEl) {
21810             this.updatePosition(this.placement, true);
21811              
21812         } else {
21813             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21814             var es = this.el.getSize();
21815             var x = Roo.lib.Dom.getViewWidth()/2;
21816             var y = Roo.lib.Dom.getViewHeight()/2;
21817             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21818             
21819         }
21820
21821          
21822          
21823         
21824         
21825     },
21826     
21827     /**
21828      * Show the popover
21829      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21830      * @param {string} (left|right|top|bottom) position
21831      */
21832     show : function (on_el, placement)
21833     {
21834         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21835         on_el = on_el || false; // default to false
21836          
21837         if (!on_el) {
21838             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21839                 on_el = this.parent().el;
21840             } else if (this.over) {
21841                 on_el = Roo.get(this.over);
21842             }
21843             
21844         }
21845         
21846         this.alignEl = Roo.get( on_el );
21847
21848         if (!this.el) {
21849             this.render(document.body);
21850         }
21851         
21852         
21853          
21854         
21855         if (this.title === false) {
21856             this.headerEl.hide();
21857         }
21858         
21859        
21860         this.el.show();
21861         this.el.dom.style.display = 'block';
21862          
21863         this.doAlign();
21864         
21865         //var arrow = this.el.select('.arrow',true).first();
21866         //arrow.set(align[2], 
21867         
21868         this.el.addClass('in');
21869         
21870          
21871         
21872         this.hoverState = 'in';
21873         
21874         if (this.modal) {
21875             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21876             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21877             this.maskEl.dom.style.display = 'block';
21878             this.maskEl.addClass('show');
21879         }
21880         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21881  
21882         this.fireEvent('show', this);
21883         
21884     },
21885     /**
21886      * fire this manually after loading a grid in the table for example
21887      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21888      * @param {Boolean} try and move it if we cant get right position.
21889      */
21890     updatePosition : function(placement, try_move)
21891     {
21892         // allow for calling with no parameters
21893         placement = placement   ? placement :  this.placement;
21894         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21895         
21896         this.el.removeClass([
21897             'fade','top','bottom', 'left', 'right','in',
21898             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21899         ]);
21900         this.el.addClass(placement + ' bs-popover-' + placement);
21901         
21902         if (!this.alignEl ) {
21903             return false;
21904         }
21905         
21906         switch (placement) {
21907             case 'right':
21908                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21909                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21910                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21911                     //normal display... or moved up/down.
21912                     this.el.setXY(offset);
21913                     var xy = this.alignEl.getAnchorXY('tr', false);
21914                     xy[0]+=2;xy[1]+=5;
21915                     this.arrowEl.setXY(xy);
21916                     return true;
21917                 }
21918                 // continue through...
21919                 return this.updatePosition('left', false);
21920                 
21921             
21922             case 'left':
21923                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21924                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21925                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21926                     //normal display... or moved up/down.
21927                     this.el.setXY(offset);
21928                     var xy = this.alignEl.getAnchorXY('tl', false);
21929                     xy[0]-=10;xy[1]+=5; // << fix me
21930                     this.arrowEl.setXY(xy);
21931                     return true;
21932                 }
21933                 // call self...
21934                 return this.updatePosition('right', false);
21935             
21936             case 'top':
21937                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21938                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21939                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21940                     //normal display... or moved up/down.
21941                     this.el.setXY(offset);
21942                     var xy = this.alignEl.getAnchorXY('t', false);
21943                     xy[1]-=10; // << fix me
21944                     this.arrowEl.setXY(xy);
21945                     return true;
21946                 }
21947                 // fall through
21948                return this.updatePosition('bottom', false);
21949             
21950             case 'bottom':
21951                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21952                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21953                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21954                     //normal display... or moved up/down.
21955                     this.el.setXY(offset);
21956                     var xy = this.alignEl.getAnchorXY('b', false);
21957                      xy[1]+=2; // << fix me
21958                     this.arrowEl.setXY(xy);
21959                     return true;
21960                 }
21961                 // fall through
21962                 return this.updatePosition('top', false);
21963                 
21964             
21965         }
21966         
21967         
21968         return false;
21969     },
21970     
21971     hide : function()
21972     {
21973         this.el.setXY([0,0]);
21974         this.el.removeClass('in');
21975         this.el.hide();
21976         this.hoverState = null;
21977         this.maskEl.hide(); // always..
21978         this.fireEvent('hide', this);
21979     }
21980     
21981 });
21982
21983
21984 Roo.apply(Roo.bootstrap.Popover, {
21985
21986     alignment : {
21987         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21988         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21989         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21990         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21991     },
21992     
21993     zIndex : 20001,
21994
21995     clickHander : false,
21996     
21997     
21998
21999     onMouseDown : function(e)
22000     {
22001         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
22002             /// what is nothing is showing..
22003             this.hideAll();
22004         }
22005          
22006     },
22007     
22008     
22009     popups : [],
22010     
22011     register : function(popup)
22012     {
22013         if (!Roo.bootstrap.Popover.clickHandler) {
22014             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22015         }
22016         // hide other popups.
22017         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
22018         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
22019         this.hideAll(); //<< why?
22020         //this.popups.push(popup);
22021     },
22022     hideAll : function()
22023     {
22024         this.popups.forEach(function(p) {
22025             p.hide();
22026         });
22027     },
22028     onShow : function() {
22029         Roo.bootstrap.Popover.popups.push(this);
22030     },
22031     onHide : function() {
22032         Roo.bootstrap.Popover.popups.remove(this);
22033     } 
22034
22035 });
22036 /**
22037  * @class Roo.bootstrap.PopoverNav
22038  * @extends Roo.bootstrap.nav.Simplebar
22039  * @parent Roo.bootstrap.Popover
22040  * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22041  * @licence LGPL
22042  * Bootstrap Popover header navigation class
22043  * FIXME? should this go under nav?
22044  *
22045  * 
22046  * @constructor
22047  * Create a new Popover Header Navigation 
22048  * @param {Object} config The config object
22049  */
22050
22051 Roo.bootstrap.PopoverNav = function(config){
22052     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22053 };
22054
22055 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar,  {
22056     
22057     
22058     container_method : 'getPopoverHeader' 
22059     
22060      
22061     
22062     
22063    
22064 });
22065
22066  
22067
22068  /*
22069  * - LGPL
22070  *
22071  * Progress
22072  * 
22073  */
22074
22075 /**
22076  * @class Roo.bootstrap.Progress
22077  * @extends Roo.bootstrap.Component
22078  * @children Roo.bootstrap.ProgressBar
22079  * Bootstrap Progress class
22080  * @cfg {Boolean} striped striped of the progress bar
22081  * @cfg {Boolean} active animated of the progress bar
22082  * 
22083  * 
22084  * @constructor
22085  * Create a new Progress
22086  * @param {Object} config The config object
22087  */
22088
22089 Roo.bootstrap.Progress = function(config){
22090     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22091 };
22092
22093 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
22094     
22095     striped : false,
22096     active: false,
22097     
22098     getAutoCreate : function(){
22099         var cfg = {
22100             tag: 'div',
22101             cls: 'progress'
22102         };
22103         
22104         
22105         if(this.striped){
22106             cfg.cls += ' progress-striped';
22107         }
22108       
22109         if(this.active){
22110             cfg.cls += ' active';
22111         }
22112         
22113         
22114         return cfg;
22115     }
22116    
22117 });
22118
22119  
22120
22121  /*
22122  * - LGPL
22123  *
22124  * ProgressBar
22125  * 
22126  */
22127
22128 /**
22129  * @class Roo.bootstrap.ProgressBar
22130  * @extends Roo.bootstrap.Component
22131  * Bootstrap ProgressBar class
22132  * @cfg {Number} aria_valuenow aria-value now
22133  * @cfg {Number} aria_valuemin aria-value min
22134  * @cfg {Number} aria_valuemax aria-value max
22135  * @cfg {String} label label for the progress bar
22136  * @cfg {String} panel (success | info | warning | danger )
22137  * @cfg {String} role role of the progress bar
22138  * @cfg {String} sr_only text
22139  * 
22140  * 
22141  * @constructor
22142  * Create a new ProgressBar
22143  * @param {Object} config The config object
22144  */
22145
22146 Roo.bootstrap.ProgressBar = function(config){
22147     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22148 };
22149
22150 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
22151     
22152     aria_valuenow : 0,
22153     aria_valuemin : 0,
22154     aria_valuemax : 100,
22155     label : false,
22156     panel : false,
22157     role : false,
22158     sr_only: false,
22159     
22160     getAutoCreate : function()
22161     {
22162         
22163         var cfg = {
22164             tag: 'div',
22165             cls: 'progress-bar',
22166             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22167         };
22168         
22169         if(this.sr_only){
22170             cfg.cn = {
22171                 tag: 'span',
22172                 cls: 'sr-only',
22173                 html: this.sr_only
22174             }
22175         }
22176         
22177         if(this.role){
22178             cfg.role = this.role;
22179         }
22180         
22181         if(this.aria_valuenow){
22182             cfg['aria-valuenow'] = this.aria_valuenow;
22183         }
22184         
22185         if(this.aria_valuemin){
22186             cfg['aria-valuemin'] = this.aria_valuemin;
22187         }
22188         
22189         if(this.aria_valuemax){
22190             cfg['aria-valuemax'] = this.aria_valuemax;
22191         }
22192         
22193         if(this.label && !this.sr_only){
22194             cfg.html = this.label;
22195         }
22196         
22197         if(this.panel){
22198             cfg.cls += ' progress-bar-' + this.panel;
22199         }
22200         
22201         return cfg;
22202     },
22203     
22204     update : function(aria_valuenow)
22205     {
22206         this.aria_valuenow = aria_valuenow;
22207         
22208         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22209     }
22210    
22211 });
22212
22213  
22214
22215  /**
22216  * @class Roo.bootstrap.TabGroup
22217  * @extends Roo.bootstrap.Column
22218  * @children Roo.bootstrap.TabPanel
22219  * Bootstrap Column class
22220  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22221  * @cfg {Boolean} carousel true to make the group behave like a carousel
22222  * @cfg {Boolean} bullets show bullets for the panels
22223  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22224  * @cfg {Number} timer auto slide timer .. default 0 millisecond
22225  * @cfg {Boolean} showarrow (true|false) show arrow default true
22226  * 
22227  * @constructor
22228  * Create a new TabGroup
22229  * @param {Object} config The config object
22230  */
22231
22232 Roo.bootstrap.TabGroup = function(config){
22233     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22234     if (!this.navId) {
22235         this.navId = Roo.id();
22236     }
22237     this.tabs = [];
22238     Roo.bootstrap.TabGroup.register(this);
22239     
22240 };
22241
22242 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
22243     
22244     carousel : false,
22245     transition : false,
22246     bullets : 0,
22247     timer : 0,
22248     autoslide : false,
22249     slideFn : false,
22250     slideOnTouch : false,
22251     showarrow : true,
22252     
22253     getAutoCreate : function()
22254     {
22255         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22256         
22257         cfg.cls += ' tab-content';
22258         
22259         if (this.carousel) {
22260             cfg.cls += ' carousel slide';
22261             
22262             cfg.cn = [{
22263                cls : 'carousel-inner',
22264                cn : []
22265             }];
22266         
22267             if(this.bullets  && !Roo.isTouch){
22268                 
22269                 var bullets = {
22270                     cls : 'carousel-bullets',
22271                     cn : []
22272                 };
22273                
22274                 if(this.bullets_cls){
22275                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22276                 }
22277                 
22278                 bullets.cn.push({
22279                     cls : 'clear'
22280                 });
22281                 
22282                 cfg.cn[0].cn.push(bullets);
22283             }
22284             
22285             if(this.showarrow){
22286                 cfg.cn[0].cn.push({
22287                     tag : 'div',
22288                     class : 'carousel-arrow',
22289                     cn : [
22290                         {
22291                             tag : 'div',
22292                             class : 'carousel-prev',
22293                             cn : [
22294                                 {
22295                                     tag : 'i',
22296                                     class : 'fa fa-chevron-left'
22297                                 }
22298                             ]
22299                         },
22300                         {
22301                             tag : 'div',
22302                             class : 'carousel-next',
22303                             cn : [
22304                                 {
22305                                     tag : 'i',
22306                                     class : 'fa fa-chevron-right'
22307                                 }
22308                             ]
22309                         }
22310                     ]
22311                 });
22312             }
22313             
22314         }
22315         
22316         return cfg;
22317     },
22318     
22319     initEvents:  function()
22320     {
22321 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22322 //            this.el.on("touchstart", this.onTouchStart, this);
22323 //        }
22324         
22325         if(this.autoslide){
22326             var _this = this;
22327             
22328             this.slideFn = window.setInterval(function() {
22329                 _this.showPanelNext();
22330             }, this.timer);
22331         }
22332         
22333         if(this.showarrow){
22334             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22335             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22336         }
22337         
22338         
22339     },
22340     
22341 //    onTouchStart : function(e, el, o)
22342 //    {
22343 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22344 //            return;
22345 //        }
22346 //        
22347 //        this.showPanelNext();
22348 //    },
22349     
22350     
22351     getChildContainer : function()
22352     {
22353         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22354     },
22355     
22356     /**
22357     * register a Navigation item
22358     * @param {Roo.bootstrap.nav.Item} the navitem to add
22359     */
22360     register : function(item)
22361     {
22362         this.tabs.push( item);
22363         item.navId = this.navId; // not really needed..
22364         this.addBullet();
22365     
22366     },
22367     
22368     getActivePanel : function()
22369     {
22370         var r = false;
22371         Roo.each(this.tabs, function(t) {
22372             if (t.active) {
22373                 r = t;
22374                 return false;
22375             }
22376             return null;
22377         });
22378         return r;
22379         
22380     },
22381     getPanelByName : function(n)
22382     {
22383         var r = false;
22384         Roo.each(this.tabs, function(t) {
22385             if (t.tabId == n) {
22386                 r = t;
22387                 return false;
22388             }
22389             return null;
22390         });
22391         return r;
22392     },
22393     indexOfPanel : function(p)
22394     {
22395         var r = false;
22396         Roo.each(this.tabs, function(t,i) {
22397             if (t.tabId == p.tabId) {
22398                 r = i;
22399                 return false;
22400             }
22401             return null;
22402         });
22403         return r;
22404     },
22405     /**
22406      * show a specific panel
22407      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22408      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22409      */
22410     showPanel : function (pan)
22411     {
22412         if(this.transition || typeof(pan) == 'undefined'){
22413             Roo.log("waiting for the transitionend");
22414             return false;
22415         }
22416         
22417         if (typeof(pan) == 'number') {
22418             pan = this.tabs[pan];
22419         }
22420         
22421         if (typeof(pan) == 'string') {
22422             pan = this.getPanelByName(pan);
22423         }
22424         
22425         var cur = this.getActivePanel();
22426         
22427         if(!pan || !cur){
22428             Roo.log('pan or acitve pan is undefined');
22429             return false;
22430         }
22431         
22432         if (pan.tabId == this.getActivePanel().tabId) {
22433             return true;
22434         }
22435         
22436         if (false === cur.fireEvent('beforedeactivate')) {
22437             return false;
22438         }
22439         
22440         if(this.bullets > 0 && !Roo.isTouch){
22441             this.setActiveBullet(this.indexOfPanel(pan));
22442         }
22443         
22444         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22445             
22446             //class="carousel-item carousel-item-next carousel-item-left"
22447             
22448             this.transition = true;
22449             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
22450             var lr = dir == 'next' ? 'left' : 'right';
22451             pan.el.addClass(dir); // or prev
22452             pan.el.addClass('carousel-item-' + dir); // or prev
22453             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22454             cur.el.addClass(lr); // or right
22455             pan.el.addClass(lr);
22456             cur.el.addClass('carousel-item-' +lr); // or right
22457             pan.el.addClass('carousel-item-' +lr);
22458             
22459             
22460             var _this = this;
22461             cur.el.on('transitionend', function() {
22462                 Roo.log("trans end?");
22463                 
22464                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22465                 pan.setActive(true);
22466                 
22467                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22468                 cur.setActive(false);
22469                 
22470                 _this.transition = false;
22471                 
22472             }, this, { single:  true } );
22473             
22474             return true;
22475         }
22476         
22477         cur.setActive(false);
22478         pan.setActive(true);
22479         
22480         return true;
22481         
22482     },
22483     showPanelNext : function()
22484     {
22485         var i = this.indexOfPanel(this.getActivePanel());
22486         
22487         if (i >= this.tabs.length - 1 && !this.autoslide) {
22488             return;
22489         }
22490         
22491         if (i >= this.tabs.length - 1 && this.autoslide) {
22492             i = -1;
22493         }
22494         
22495         this.showPanel(this.tabs[i+1]);
22496     },
22497     
22498     showPanelPrev : function()
22499     {
22500         var i = this.indexOfPanel(this.getActivePanel());
22501         
22502         if (i  < 1 && !this.autoslide) {
22503             return;
22504         }
22505         
22506         if (i < 1 && this.autoslide) {
22507             i = this.tabs.length;
22508         }
22509         
22510         this.showPanel(this.tabs[i-1]);
22511     },
22512     
22513     
22514     addBullet: function()
22515     {
22516         if(!this.bullets || Roo.isTouch){
22517             return;
22518         }
22519         var ctr = this.el.select('.carousel-bullets',true).first();
22520         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22521         var bullet = ctr.createChild({
22522             cls : 'bullet bullet-' + i
22523         },ctr.dom.lastChild);
22524         
22525         
22526         var _this = this;
22527         
22528         bullet.on('click', (function(e, el, o, ii, t){
22529
22530             e.preventDefault();
22531
22532             this.showPanel(ii);
22533
22534             if(this.autoslide && this.slideFn){
22535                 clearInterval(this.slideFn);
22536                 this.slideFn = window.setInterval(function() {
22537                     _this.showPanelNext();
22538                 }, this.timer);
22539             }
22540
22541         }).createDelegate(this, [i, bullet], true));
22542                 
22543         
22544     },
22545      
22546     setActiveBullet : function(i)
22547     {
22548         if(Roo.isTouch){
22549             return;
22550         }
22551         
22552         Roo.each(this.el.select('.bullet', true).elements, function(el){
22553             el.removeClass('selected');
22554         });
22555
22556         var bullet = this.el.select('.bullet-' + i, true).first();
22557         
22558         if(!bullet){
22559             return;
22560         }
22561         
22562         bullet.addClass('selected');
22563     }
22564     
22565     
22566   
22567 });
22568
22569  
22570
22571  
22572  
22573 Roo.apply(Roo.bootstrap.TabGroup, {
22574     
22575     groups: {},
22576      /**
22577     * register a Navigation Group
22578     * @param {Roo.bootstrap.nav.Group} the navgroup to add
22579     */
22580     register : function(navgrp)
22581     {
22582         this.groups[navgrp.navId] = navgrp;
22583         
22584     },
22585     /**
22586     * fetch a Navigation Group based on the navigation ID
22587     * if one does not exist , it will get created.
22588     * @param {string} the navgroup to add
22589     * @returns {Roo.bootstrap.nav.Group} the navgroup 
22590     */
22591     get: function(navId) {
22592         if (typeof(this.groups[navId]) == 'undefined') {
22593             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22594         }
22595         return this.groups[navId] ;
22596     }
22597     
22598     
22599     
22600 });
22601
22602  /*
22603  * - LGPL
22604  *
22605  * TabPanel
22606  * 
22607  */
22608
22609 /**
22610  * @class Roo.bootstrap.TabPanel
22611  * @extends Roo.bootstrap.Component
22612  * @children Roo.bootstrap.Component
22613  * Bootstrap TabPanel class
22614  * @cfg {Boolean} active panel active
22615  * @cfg {String} html panel content
22616  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22617  * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22618  * @cfg {String} href click to link..
22619  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22620  * 
22621  * 
22622  * @constructor
22623  * Create a new TabPanel
22624  * @param {Object} config The config object
22625  */
22626
22627 Roo.bootstrap.TabPanel = function(config){
22628     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22629     this.addEvents({
22630         /**
22631              * @event changed
22632              * Fires when the active status changes
22633              * @param {Roo.bootstrap.TabPanel} this
22634              * @param {Boolean} state the new state
22635             
22636          */
22637         'changed': true,
22638         /**
22639              * @event beforedeactivate
22640              * Fires before a tab is de-activated - can be used to do validation on a form.
22641              * @param {Roo.bootstrap.TabPanel} this
22642              * @return {Boolean} false if there is an error
22643             
22644          */
22645         'beforedeactivate': true
22646      });
22647     
22648     this.tabId = this.tabId || Roo.id();
22649   
22650 };
22651
22652 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22653     
22654     active: false,
22655     html: false,
22656     tabId: false,
22657     navId : false,
22658     href : '',
22659     touchSlide : false,
22660     getAutoCreate : function(){
22661         
22662         
22663         var cfg = {
22664             tag: 'div',
22665             // item is needed for carousel - not sure if it has any effect otherwise
22666             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22667             html: this.html || ''
22668         };
22669         
22670         if(this.active){
22671             cfg.cls += ' active';
22672         }
22673         
22674         if(this.tabId){
22675             cfg.tabId = this.tabId;
22676         }
22677         
22678         
22679         
22680         return cfg;
22681     },
22682     
22683     initEvents:  function()
22684     {
22685         var p = this.parent();
22686         
22687         this.navId = this.navId || p.navId;
22688         
22689         if (typeof(this.navId) != 'undefined') {
22690             // not really needed.. but just in case.. parent should be a NavGroup.
22691             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22692             
22693             tg.register(this);
22694             
22695             var i = tg.tabs.length - 1;
22696             
22697             if(this.active && tg.bullets > 0 && i < tg.bullets){
22698                 tg.setActiveBullet(i);
22699             }
22700         }
22701         
22702         this.el.on('click', this.onClick, this);
22703         
22704         if(Roo.isTouch && this.touchSlide){
22705             this.el.on("touchstart", this.onTouchStart, this);
22706             this.el.on("touchmove", this.onTouchMove, this);
22707             this.el.on("touchend", this.onTouchEnd, this);
22708         }
22709         
22710     },
22711     
22712     onRender : function(ct, position)
22713     {
22714         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22715     },
22716     
22717     setActive : function(state)
22718     {
22719         Roo.log("panel - set active " + this.tabId + "=" + state);
22720         
22721         this.active = state;
22722         if (!state) {
22723             this.el.removeClass('active');
22724             
22725         } else  if (!this.el.hasClass('active')) {
22726             this.el.addClass('active');
22727         }
22728         
22729         this.fireEvent('changed', this, state);
22730     },
22731     
22732     onClick : function(e)
22733     {
22734         e.preventDefault();
22735         
22736         if(!this.href.length){
22737             return;
22738         }
22739         
22740         window.location.href = this.href;
22741     },
22742     
22743     startX : 0,
22744     startY : 0,
22745     endX : 0,
22746     endY : 0,
22747     swiping : false,
22748     
22749     onTouchStart : function(e)
22750     {
22751         this.swiping = false;
22752         
22753         this.startX = e.browserEvent.touches[0].clientX;
22754         this.startY = e.browserEvent.touches[0].clientY;
22755     },
22756     
22757     onTouchMove : function(e)
22758     {
22759         this.swiping = true;
22760         
22761         this.endX = e.browserEvent.touches[0].clientX;
22762         this.endY = e.browserEvent.touches[0].clientY;
22763     },
22764     
22765     onTouchEnd : function(e)
22766     {
22767         if(!this.swiping){
22768             this.onClick(e);
22769             return;
22770         }
22771         
22772         var tabGroup = this.parent();
22773         
22774         if(this.endX > this.startX){ // swiping right
22775             tabGroup.showPanelPrev();
22776             return;
22777         }
22778         
22779         if(this.startX > this.endX){ // swiping left
22780             tabGroup.showPanelNext();
22781             return;
22782         }
22783     }
22784     
22785     
22786 });
22787  
22788
22789  
22790
22791  /*
22792  * - LGPL
22793  *
22794  * DateField
22795  * 
22796  */
22797
22798 /**
22799  * @class Roo.bootstrap.form.DateField
22800  * @extends Roo.bootstrap.form.Input
22801  * Bootstrap DateField class
22802  * @cfg {Number} weekStart default 0
22803  * @cfg {String} viewMode default empty, (months|years)
22804  * @cfg {String} minViewMode default empty, (months|years)
22805  * @cfg {Number} startDate default -Infinity
22806  * @cfg {Number} endDate default Infinity
22807  * @cfg {Boolean} todayHighlight default false
22808  * @cfg {Boolean} todayBtn default false
22809  * @cfg {Boolean} calendarWeeks default false
22810  * @cfg {Object} daysOfWeekDisabled default empty
22811  * @cfg {Boolean} singleMode default false (true | false)
22812  * 
22813  * @cfg {Boolean} keyboardNavigation default true
22814  * @cfg {String} language default en
22815  * 
22816  * @constructor
22817  * Create a new DateField
22818  * @param {Object} config The config object
22819  */
22820
22821 Roo.bootstrap.form.DateField = function(config){
22822     Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22823      this.addEvents({
22824             /**
22825              * @event show
22826              * Fires when this field show.
22827              * @param {Roo.bootstrap.form.DateField} this
22828              * @param {Mixed} date The date value
22829              */
22830             show : true,
22831             /**
22832              * @event show
22833              * Fires when this field hide.
22834              * @param {Roo.bootstrap.form.DateField} this
22835              * @param {Mixed} date The date value
22836              */
22837             hide : true,
22838             /**
22839              * @event select
22840              * Fires when select a date.
22841              * @param {Roo.bootstrap.form.DateField} this
22842              * @param {Mixed} date The date value
22843              */
22844             select : true,
22845             /**
22846              * @event beforeselect
22847              * Fires when before select a date.
22848              * @param {Roo.bootstrap.form.DateField} this
22849              * @param {Mixed} date The date value
22850              */
22851             beforeselect : true
22852         });
22853 };
22854
22855 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input,  {
22856     
22857     /**
22858      * @cfg {String} format
22859      * The default date format string which can be overriden for localization support.  The format must be
22860      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22861      */
22862     format : "m/d/y",
22863     /**
22864      * @cfg {String} altFormats
22865      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22866      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22867      */
22868     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22869     
22870     weekStart : 0,
22871     
22872     viewMode : '',
22873     
22874     minViewMode : '',
22875     
22876     todayHighlight : false,
22877     
22878     todayBtn: false,
22879     
22880     language: 'en',
22881     
22882     keyboardNavigation: true,
22883     
22884     calendarWeeks: false,
22885     
22886     startDate: -Infinity,
22887     
22888     endDate: Infinity,
22889     
22890     daysOfWeekDisabled: [],
22891     
22892     _events: [],
22893     
22894     singleMode : false,
22895     
22896     UTCDate: function()
22897     {
22898         return new Date(Date.UTC.apply(Date, arguments));
22899     },
22900     
22901     UTCToday: function()
22902     {
22903         var today = new Date();
22904         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22905     },
22906     
22907     getDate: function() {
22908             var d = this.getUTCDate();
22909             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22910     },
22911     
22912     getUTCDate: function() {
22913             return this.date;
22914     },
22915     
22916     setDate: function(d) {
22917             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22918     },
22919     
22920     setUTCDate: function(d) {
22921             this.date = d;
22922             this.setValue(this.formatDate(this.date));
22923     },
22924         
22925     onRender: function(ct, position)
22926     {
22927         
22928         Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22929         
22930         this.language = this.language || 'en';
22931         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22932         this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22933         
22934         this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22935         this.format = this.format || 'm/d/y';
22936         this.isInline = false;
22937         this.isInput = true;
22938         this.component = this.el.select('.add-on', true).first() || false;
22939         this.component = (this.component && this.component.length === 0) ? false : this.component;
22940         this.hasInput = this.component && this.inputEl().length;
22941         
22942         if (typeof(this.minViewMode === 'string')) {
22943             switch (this.minViewMode) {
22944                 case 'months':
22945                     this.minViewMode = 1;
22946                     break;
22947                 case 'years':
22948                     this.minViewMode = 2;
22949                     break;
22950                 default:
22951                     this.minViewMode = 0;
22952                     break;
22953             }
22954         }
22955         
22956         if (typeof(this.viewMode === 'string')) {
22957             switch (this.viewMode) {
22958                 case 'months':
22959                     this.viewMode = 1;
22960                     break;
22961                 case 'years':
22962                     this.viewMode = 2;
22963                     break;
22964                 default:
22965                     this.viewMode = 0;
22966                     break;
22967             }
22968         }
22969                 
22970         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22971         
22972 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22973         
22974         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22975         
22976         this.picker().on('mousedown', this.onMousedown, this);
22977         this.picker().on('click', this.onClick, this);
22978         
22979         this.picker().addClass('datepicker-dropdown');
22980         
22981         this.startViewMode = this.viewMode;
22982         
22983         if(this.singleMode){
22984             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22985                 v.setVisibilityMode(Roo.Element.DISPLAY);
22986                 v.hide();
22987             });
22988             
22989             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22990                 v.setStyle('width', '189px');
22991             });
22992         }
22993         
22994         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22995             if(!this.calendarWeeks){
22996                 v.remove();
22997                 return;
22998             }
22999             
23000             v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23001             v.attr('colspan', function(i, val){
23002                 return parseInt(val) + 1;
23003             });
23004         });
23005                         
23006         
23007         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23008         
23009         this.setStartDate(this.startDate);
23010         this.setEndDate(this.endDate);
23011         
23012         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23013         
23014         this.fillDow();
23015         this.fillMonths();
23016         this.update();
23017         this.showMode();
23018         
23019         if(this.isInline) {
23020             this.showPopup();
23021         }
23022     },
23023     
23024     picker : function()
23025     {
23026         return this.pickerEl;
23027 //        return this.el.select('.datepicker', true).first();
23028     },
23029     
23030     fillDow: function()
23031     {
23032         var dowCnt = this.weekStart;
23033         
23034         var dow = {
23035             tag: 'tr',
23036             cn: [
23037                 
23038             ]
23039         };
23040         
23041         if(this.calendarWeeks){
23042             dow.cn.push({
23043                 tag: 'th',
23044                 cls: 'cw',
23045                 html: '&nbsp;'
23046             })
23047         }
23048         
23049         while (dowCnt < this.weekStart + 7) {
23050             dow.cn.push({
23051                 tag: 'th',
23052                 cls: 'dow',
23053                 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23054             });
23055         }
23056         
23057         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23058     },
23059     
23060     fillMonths: function()
23061     {    
23062         var i = 0;
23063         var months = this.picker().select('>.datepicker-months td', true).first();
23064         
23065         months.dom.innerHTML = '';
23066         
23067         while (i < 12) {
23068             var month = {
23069                 tag: 'span',
23070                 cls: 'month',
23071                 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23072             };
23073             
23074             months.createChild(month);
23075         }
23076         
23077     },
23078     
23079     update: function()
23080     {
23081         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;
23082         
23083         if (this.date < this.startDate) {
23084             this.viewDate = new Date(this.startDate);
23085         } else if (this.date > this.endDate) {
23086             this.viewDate = new Date(this.endDate);
23087         } else {
23088             this.viewDate = new Date(this.date);
23089         }
23090         
23091         this.fill();
23092     },
23093     
23094     fill: function() 
23095     {
23096         var d = new Date(this.viewDate),
23097                 year = d.getUTCFullYear(),
23098                 month = d.getUTCMonth(),
23099                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23100                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23101                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23102                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23103                 currentDate = this.date && this.date.valueOf(),
23104                 today = this.UTCToday();
23105         
23106         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23107         
23108 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23109         
23110 //        this.picker.select('>tfoot th.today').
23111 //                                              .text(dates[this.language].today)
23112 //                                              .toggle(this.todayBtn !== false);
23113     
23114         this.updateNavArrows();
23115         this.fillMonths();
23116                                                 
23117         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23118         
23119         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23120          
23121         prevMonth.setUTCDate(day);
23122         
23123         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23124         
23125         var nextMonth = new Date(prevMonth);
23126         
23127         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23128         
23129         nextMonth = nextMonth.valueOf();
23130         
23131         var fillMonths = false;
23132         
23133         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23134         
23135         while(prevMonth.valueOf() <= nextMonth) {
23136             var clsName = '';
23137             
23138             if (prevMonth.getUTCDay() === this.weekStart) {
23139                 if(fillMonths){
23140                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23141                 }
23142                     
23143                 fillMonths = {
23144                     tag: 'tr',
23145                     cn: []
23146                 };
23147                 
23148                 if(this.calendarWeeks){
23149                     // ISO 8601: First week contains first thursday.
23150                     // ISO also states week starts on Monday, but we can be more abstract here.
23151                     var
23152                     // Start of current week: based on weekstart/current date
23153                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23154                     // Thursday of this week
23155                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23156                     // First Thursday of year, year from thursday
23157                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23158                     // Calendar week: ms between thursdays, div ms per day, div 7 days
23159                     calWeek =  (th - yth) / 864e5 / 7 + 1;
23160                     
23161                     fillMonths.cn.push({
23162                         tag: 'td',
23163                         cls: 'cw',
23164                         html: calWeek
23165                     });
23166                 }
23167             }
23168             
23169             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23170                 clsName += ' old';
23171             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23172                 clsName += ' new';
23173             }
23174             if (this.todayHighlight &&
23175                 prevMonth.getUTCFullYear() == today.getFullYear() &&
23176                 prevMonth.getUTCMonth() == today.getMonth() &&
23177                 prevMonth.getUTCDate() == today.getDate()) {
23178                 clsName += ' today';
23179             }
23180             
23181             if (currentDate && prevMonth.valueOf() === currentDate) {
23182                 clsName += ' active';
23183             }
23184             
23185             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23186                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23187                     clsName += ' disabled';
23188             }
23189             
23190             fillMonths.cn.push({
23191                 tag: 'td',
23192                 cls: 'day ' + clsName,
23193                 html: prevMonth.getDate()
23194             });
23195             
23196             prevMonth.setDate(prevMonth.getDate()+1);
23197         }
23198           
23199         var currentYear = this.date && this.date.getUTCFullYear();
23200         var currentMonth = this.date && this.date.getUTCMonth();
23201         
23202         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23203         
23204         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23205             v.removeClass('active');
23206             
23207             if(currentYear === year && k === currentMonth){
23208                 v.addClass('active');
23209             }
23210             
23211             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23212                 v.addClass('disabled');
23213             }
23214             
23215         });
23216         
23217         
23218         year = parseInt(year/10, 10) * 10;
23219         
23220         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23221         
23222         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23223         
23224         year -= 1;
23225         for (var i = -1; i < 11; i++) {
23226             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23227                 tag: 'span',
23228                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23229                 html: year
23230             });
23231             
23232             year += 1;
23233         }
23234     },
23235     
23236     showMode: function(dir) 
23237     {
23238         if (dir) {
23239             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23240         }
23241         
23242         Roo.each(this.picker().select('>div',true).elements, function(v){
23243             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23244             v.hide();
23245         });
23246         this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23247     },
23248     
23249     place: function()
23250     {
23251         if(this.isInline) {
23252             return;
23253         }
23254         
23255         this.picker().removeClass(['bottom', 'top']);
23256         
23257         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23258             /*
23259              * place to the top of element!
23260              *
23261              */
23262             
23263             this.picker().addClass('top');
23264             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23265             
23266             return;
23267         }
23268         
23269         this.picker().addClass('bottom');
23270         
23271         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23272     },
23273     
23274     parseDate : function(value)
23275     {
23276         if(!value || value instanceof Date){
23277             return value;
23278         }
23279         var v = Date.parseDate(value, this.format);
23280         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23281             v = Date.parseDate(value, 'Y-m-d');
23282         }
23283         if(!v && this.altFormats){
23284             if(!this.altFormatsArray){
23285                 this.altFormatsArray = this.altFormats.split("|");
23286             }
23287             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23288                 v = Date.parseDate(value, this.altFormatsArray[i]);
23289             }
23290         }
23291         return v;
23292     },
23293     
23294     formatDate : function(date, fmt)
23295     {   
23296         return (!date || !(date instanceof Date)) ?
23297         date : date.dateFormat(fmt || this.format);
23298     },
23299     
23300     onFocus : function()
23301     {
23302         Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23303         this.showPopup();
23304     },
23305     
23306     onBlur : function()
23307     {
23308         Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23309         
23310         var d = this.inputEl().getValue();
23311         
23312         this.setValue(d);
23313                 
23314         this.hidePopup();
23315     },
23316     
23317     showPopup : function()
23318     {
23319         this.picker().show();
23320         this.update();
23321         this.place();
23322         
23323         this.fireEvent('showpopup', this, this.date);
23324     },
23325     
23326     hidePopup : function()
23327     {
23328         if(this.isInline) {
23329             return;
23330         }
23331         this.picker().hide();
23332         this.viewMode = this.startViewMode;
23333         this.showMode();
23334         
23335         this.fireEvent('hidepopup', this, this.date);
23336         
23337     },
23338     
23339     onMousedown: function(e)
23340     {
23341         e.stopPropagation();
23342         e.preventDefault();
23343     },
23344     
23345     keyup: function(e)
23346     {
23347         Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23348         this.update();
23349     },
23350
23351     setValue: function(v)
23352     {
23353         if(this.fireEvent('beforeselect', this, v) !== false){
23354             var d = new Date(this.parseDate(v) ).clearTime();
23355         
23356             if(isNaN(d.getTime())){
23357                 this.date = this.viewDate = '';
23358                 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23359                 return;
23360             }
23361
23362             v = this.formatDate(d);
23363
23364             Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23365
23366             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23367
23368             this.update();
23369
23370             this.fireEvent('select', this, this.date);
23371         }
23372     },
23373     
23374     getValue: function()
23375     {
23376         return this.formatDate(this.date);
23377     },
23378     
23379     fireKey: function(e)
23380     {
23381         if (!this.picker().isVisible()){
23382             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23383                 this.showPopup();
23384             }
23385             return;
23386         }
23387         
23388         var dateChanged = false,
23389         dir, day, month,
23390         newDate, newViewDate;
23391         
23392         switch(e.keyCode){
23393             case 27: // escape
23394                 this.hidePopup();
23395                 e.preventDefault();
23396                 break;
23397             case 37: // left
23398             case 39: // right
23399                 if (!this.keyboardNavigation) {
23400                     break;
23401                 }
23402                 dir = e.keyCode == 37 ? -1 : 1;
23403                 
23404                 if (e.ctrlKey){
23405                     newDate = this.moveYear(this.date, dir);
23406                     newViewDate = this.moveYear(this.viewDate, dir);
23407                 } else if (e.shiftKey){
23408                     newDate = this.moveMonth(this.date, dir);
23409                     newViewDate = this.moveMonth(this.viewDate, dir);
23410                 } else {
23411                     newDate = new Date(this.date);
23412                     newDate.setUTCDate(this.date.getUTCDate() + dir);
23413                     newViewDate = new Date(this.viewDate);
23414                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23415                 }
23416                 if (this.dateWithinRange(newDate)){
23417                     this.date = newDate;
23418                     this.viewDate = newViewDate;
23419                     this.setValue(this.formatDate(this.date));
23420 //                    this.update();
23421                     e.preventDefault();
23422                     dateChanged = true;
23423                 }
23424                 break;
23425             case 38: // up
23426             case 40: // down
23427                 if (!this.keyboardNavigation) {
23428                     break;
23429                 }
23430                 dir = e.keyCode == 38 ? -1 : 1;
23431                 if (e.ctrlKey){
23432                     newDate = this.moveYear(this.date, dir);
23433                     newViewDate = this.moveYear(this.viewDate, dir);
23434                 } else if (e.shiftKey){
23435                     newDate = this.moveMonth(this.date, dir);
23436                     newViewDate = this.moveMonth(this.viewDate, dir);
23437                 } else {
23438                     newDate = new Date(this.date);
23439                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23440                     newViewDate = new Date(this.viewDate);
23441                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23442                 }
23443                 if (this.dateWithinRange(newDate)){
23444                     this.date = newDate;
23445                     this.viewDate = newViewDate;
23446                     this.setValue(this.formatDate(this.date));
23447 //                    this.update();
23448                     e.preventDefault();
23449                     dateChanged = true;
23450                 }
23451                 break;
23452             case 13: // enter
23453                 this.setValue(this.formatDate(this.date));
23454                 this.hidePopup();
23455                 e.preventDefault();
23456                 break;
23457             case 9: // tab
23458                 this.setValue(this.formatDate(this.date));
23459                 this.hidePopup();
23460                 break;
23461             case 16: // shift
23462             case 17: // ctrl
23463             case 18: // alt
23464                 break;
23465             default :
23466                 this.hidePopup();
23467                 
23468         }
23469     },
23470     
23471     
23472     onClick: function(e) 
23473     {
23474         e.stopPropagation();
23475         e.preventDefault();
23476         
23477         var target = e.getTarget();
23478         
23479         if(target.nodeName.toLowerCase() === 'i'){
23480             target = Roo.get(target).dom.parentNode;
23481         }
23482         
23483         var nodeName = target.nodeName;
23484         var className = target.className;
23485         var html = target.innerHTML;
23486         //Roo.log(nodeName);
23487         
23488         switch(nodeName.toLowerCase()) {
23489             case 'th':
23490                 switch(className) {
23491                     case 'switch':
23492                         this.showMode(1);
23493                         break;
23494                     case 'prev':
23495                     case 'next':
23496                         var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23497                         switch(this.viewMode){
23498                                 case 0:
23499                                         this.viewDate = this.moveMonth(this.viewDate, dir);
23500                                         break;
23501                                 case 1:
23502                                 case 2:
23503                                         this.viewDate = this.moveYear(this.viewDate, dir);
23504                                         break;
23505                         }
23506                         this.fill();
23507                         break;
23508                     case 'today':
23509                         var date = new Date();
23510                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23511 //                        this.fill()
23512                         this.setValue(this.formatDate(this.date));
23513                         
23514                         this.hidePopup();
23515                         break;
23516                 }
23517                 break;
23518             case 'span':
23519                 if (className.indexOf('disabled') < 0) {
23520                 if (!this.viewDate) {
23521                     this.viewDate = new Date();
23522                 }
23523                 this.viewDate.setUTCDate(1);
23524                     if (className.indexOf('month') > -1) {
23525                         this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23526                     } else {
23527                         var year = parseInt(html, 10) || 0;
23528                         this.viewDate.setUTCFullYear(year);
23529                         
23530                     }
23531                     
23532                     if(this.singleMode){
23533                         this.setValue(this.formatDate(this.viewDate));
23534                         this.hidePopup();
23535                         return;
23536                     }
23537                     
23538                     this.showMode(-1);
23539                     this.fill();
23540                 }
23541                 break;
23542                 
23543             case 'td':
23544                 //Roo.log(className);
23545                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23546                     var day = parseInt(html, 10) || 1;
23547                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23548                         month = (this.viewDate || new Date()).getUTCMonth();
23549
23550                     if (className.indexOf('old') > -1) {
23551                         if(month === 0 ){
23552                             month = 11;
23553                             year -= 1;
23554                         }else{
23555                             month -= 1;
23556                         }
23557                     } else if (className.indexOf('new') > -1) {
23558                         if (month == 11) {
23559                             month = 0;
23560                             year += 1;
23561                         } else {
23562                             month += 1;
23563                         }
23564                     }
23565                     //Roo.log([year,month,day]);
23566                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23567                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23568 //                    this.fill();
23569                     //Roo.log(this.formatDate(this.date));
23570                     this.setValue(this.formatDate(this.date));
23571                     this.hidePopup();
23572                 }
23573                 break;
23574         }
23575     },
23576     
23577     setStartDate: function(startDate)
23578     {
23579         this.startDate = startDate || -Infinity;
23580         if (this.startDate !== -Infinity) {
23581             this.startDate = this.parseDate(this.startDate);
23582         }
23583         this.update();
23584         this.updateNavArrows();
23585     },
23586
23587     setEndDate: function(endDate)
23588     {
23589         this.endDate = endDate || Infinity;
23590         if (this.endDate !== Infinity) {
23591             this.endDate = this.parseDate(this.endDate);
23592         }
23593         this.update();
23594         this.updateNavArrows();
23595     },
23596     
23597     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23598     {
23599         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23600         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23601             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23602         }
23603         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23604             return parseInt(d, 10);
23605         });
23606         this.update();
23607         this.updateNavArrows();
23608     },
23609     
23610     updateNavArrows: function() 
23611     {
23612         if(this.singleMode){
23613             return;
23614         }
23615         
23616         var d = new Date(this.viewDate),
23617         year = d.getUTCFullYear(),
23618         month = d.getUTCMonth();
23619         
23620         Roo.each(this.picker().select('.prev', true).elements, function(v){
23621             v.show();
23622             switch (this.viewMode) {
23623                 case 0:
23624
23625                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23626                         v.hide();
23627                     }
23628                     break;
23629                 case 1:
23630                 case 2:
23631                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23632                         v.hide();
23633                     }
23634                     break;
23635             }
23636         });
23637         
23638         Roo.each(this.picker().select('.next', true).elements, function(v){
23639             v.show();
23640             switch (this.viewMode) {
23641                 case 0:
23642
23643                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23644                         v.hide();
23645                     }
23646                     break;
23647                 case 1:
23648                 case 2:
23649                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23650                         v.hide();
23651                     }
23652                     break;
23653             }
23654         })
23655     },
23656     
23657     moveMonth: function(date, dir)
23658     {
23659         if (!dir) {
23660             return date;
23661         }
23662         var new_date = new Date(date.valueOf()),
23663         day = new_date.getUTCDate(),
23664         month = new_date.getUTCMonth(),
23665         mag = Math.abs(dir),
23666         new_month, test;
23667         dir = dir > 0 ? 1 : -1;
23668         if (mag == 1){
23669             test = dir == -1
23670             // If going back one month, make sure month is not current month
23671             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23672             ? function(){
23673                 return new_date.getUTCMonth() == month;
23674             }
23675             // If going forward one month, make sure month is as expected
23676             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23677             : function(){
23678                 return new_date.getUTCMonth() != new_month;
23679             };
23680             new_month = month + dir;
23681             new_date.setUTCMonth(new_month);
23682             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23683             if (new_month < 0 || new_month > 11) {
23684                 new_month = (new_month + 12) % 12;
23685             }
23686         } else {
23687             // For magnitudes >1, move one month at a time...
23688             for (var i=0; i<mag; i++) {
23689                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23690                 new_date = this.moveMonth(new_date, dir);
23691             }
23692             // ...then reset the day, keeping it in the new month
23693             new_month = new_date.getUTCMonth();
23694             new_date.setUTCDate(day);
23695             test = function(){
23696                 return new_month != new_date.getUTCMonth();
23697             };
23698         }
23699         // Common date-resetting loop -- if date is beyond end of month, make it
23700         // end of month
23701         while (test()){
23702             new_date.setUTCDate(--day);
23703             new_date.setUTCMonth(new_month);
23704         }
23705         return new_date;
23706     },
23707
23708     moveYear: function(date, dir)
23709     {
23710         return this.moveMonth(date, dir*12);
23711     },
23712
23713     dateWithinRange: function(date)
23714     {
23715         return date >= this.startDate && date <= this.endDate;
23716     },
23717
23718     
23719     remove: function() 
23720     {
23721         this.picker().remove();
23722     },
23723     
23724     validateValue : function(value)
23725     {
23726         if(this.getVisibilityEl().hasClass('hidden')){
23727             return true;
23728         }
23729         
23730         if(value.length < 1)  {
23731             if(this.allowBlank){
23732                 return true;
23733             }
23734             return false;
23735         }
23736         
23737         if(value.length < this.minLength){
23738             return false;
23739         }
23740         if(value.length > this.maxLength){
23741             return false;
23742         }
23743         if(this.vtype){
23744             var vt = Roo.form.VTypes;
23745             if(!vt[this.vtype](value, this)){
23746                 return false;
23747             }
23748         }
23749         if(typeof this.validator == "function"){
23750             var msg = this.validator(value);
23751             if(msg !== true){
23752                 return false;
23753             }
23754         }
23755         
23756         if(this.regex && !this.regex.test(value)){
23757             return false;
23758         }
23759         
23760         if(typeof(this.parseDate(value)) == 'undefined'){
23761             return false;
23762         }
23763         
23764         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23765             return false;
23766         }      
23767         
23768         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23769             return false;
23770         } 
23771         
23772         
23773         return true;
23774     },
23775     
23776     reset : function()
23777     {
23778         this.date = this.viewDate = '';
23779         
23780         Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23781     }
23782    
23783 });
23784
23785 Roo.apply(Roo.bootstrap.form.DateField,  {
23786     
23787     head : {
23788         tag: 'thead',
23789         cn: [
23790         {
23791             tag: 'tr',
23792             cn: [
23793             {
23794                 tag: 'th',
23795                 cls: 'prev',
23796                 html: '<i class="fa fa-arrow-left"/>'
23797             },
23798             {
23799                 tag: 'th',
23800                 cls: 'switch',
23801                 colspan: '5'
23802             },
23803             {
23804                 tag: 'th',
23805                 cls: 'next',
23806                 html: '<i class="fa fa-arrow-right"/>'
23807             }
23808
23809             ]
23810         }
23811         ]
23812     },
23813     
23814     content : {
23815         tag: 'tbody',
23816         cn: [
23817         {
23818             tag: 'tr',
23819             cn: [
23820             {
23821                 tag: 'td',
23822                 colspan: '7'
23823             }
23824             ]
23825         }
23826         ]
23827     },
23828     
23829     footer : {
23830         tag: 'tfoot',
23831         cn: [
23832         {
23833             tag: 'tr',
23834             cn: [
23835             {
23836                 tag: 'th',
23837                 colspan: '7',
23838                 cls: 'today'
23839             }
23840                     
23841             ]
23842         }
23843         ]
23844     },
23845     
23846     dates:{
23847         en: {
23848             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23849             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23850             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23851             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23852             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23853             today: "Today"
23854         }
23855     },
23856     
23857     modes: [
23858     {
23859         clsName: 'days',
23860         navFnc: 'Month',
23861         navStep: 1
23862     },
23863     {
23864         clsName: 'months',
23865         navFnc: 'FullYear',
23866         navStep: 1
23867     },
23868     {
23869         clsName: 'years',
23870         navFnc: 'FullYear',
23871         navStep: 10
23872     }]
23873 });
23874
23875 Roo.apply(Roo.bootstrap.form.DateField,  {
23876   
23877     template : {
23878         tag: 'div',
23879         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23880         cn: [
23881         {
23882             tag: 'div',
23883             cls: 'datepicker-days',
23884             cn: [
23885             {
23886                 tag: 'table',
23887                 cls: 'table-condensed',
23888                 cn:[
23889                 Roo.bootstrap.form.DateField.head,
23890                 {
23891                     tag: 'tbody'
23892                 },
23893                 Roo.bootstrap.form.DateField.footer
23894                 ]
23895             }
23896             ]
23897         },
23898         {
23899             tag: 'div',
23900             cls: 'datepicker-months',
23901             cn: [
23902             {
23903                 tag: 'table',
23904                 cls: 'table-condensed',
23905                 cn:[
23906                 Roo.bootstrap.form.DateField.head,
23907                 Roo.bootstrap.form.DateField.content,
23908                 Roo.bootstrap.form.DateField.footer
23909                 ]
23910             }
23911             ]
23912         },
23913         {
23914             tag: 'div',
23915             cls: 'datepicker-years',
23916             cn: [
23917             {
23918                 tag: 'table',
23919                 cls: 'table-condensed',
23920                 cn:[
23921                 Roo.bootstrap.form.DateField.head,
23922                 Roo.bootstrap.form.DateField.content,
23923                 Roo.bootstrap.form.DateField.footer
23924                 ]
23925             }
23926             ]
23927         }
23928         ]
23929     }
23930 });
23931
23932  
23933
23934  /*
23935  * - LGPL
23936  *
23937  * TimeField
23938  * 
23939  */
23940
23941 /**
23942  * @class Roo.bootstrap.form.TimeField
23943  * @extends Roo.bootstrap.form.Input
23944  * Bootstrap DateField class
23945  * 
23946  * 
23947  * @constructor
23948  * Create a new TimeField
23949  * @param {Object} config The config object
23950  */
23951
23952 Roo.bootstrap.form.TimeField = function(config){
23953     Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23954     this.addEvents({
23955             /**
23956              * @event show
23957              * Fires when this field show.
23958              * @param {Roo.bootstrap.form.DateField} thisthis
23959              * @param {Mixed} date The date value
23960              */
23961             show : true,
23962             /**
23963              * @event show
23964              * Fires when this field hide.
23965              * @param {Roo.bootstrap.form.DateField} this
23966              * @param {Mixed} date The date value
23967              */
23968             hide : true,
23969             /**
23970              * @event select
23971              * Fires when select a date.
23972              * @param {Roo.bootstrap.form.DateField} this
23973              * @param {Mixed} date The date value
23974              */
23975             select : true
23976         });
23977 };
23978
23979 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input,  {
23980     
23981     /**
23982      * @cfg {String} format
23983      * The default time format string which can be overriden for localization support.  The format must be
23984      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23985      */
23986     format : "H:i",
23987
23988     getAutoCreate : function()
23989     {
23990         this.after = '<i class="fa far fa-clock"></i>';
23991         return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23992         
23993          
23994     },
23995     onRender: function(ct, position)
23996     {
23997         
23998         Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23999                 
24000         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24001         
24002         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24003         
24004         this.pop = this.picker().select('>.datepicker-time',true).first();
24005         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006         
24007         this.picker().on('mousedown', this.onMousedown, this);
24008         this.picker().on('click', this.onClick, this);
24009         
24010         this.picker().addClass('datepicker-dropdown');
24011     
24012         this.fillTime();
24013         this.update();
24014             
24015         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24016         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24017         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24018         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24019         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24020         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24021
24022     },
24023     
24024     fireKey: function(e){
24025         if (!this.picker().isVisible()){
24026             if (e.keyCode == 27) { // allow escape to hide and re-show picker
24027                 this.show();
24028             }
24029             return;
24030         }
24031
24032         e.preventDefault();
24033         
24034         switch(e.keyCode){
24035             case 27: // escape
24036                 this.hide();
24037                 break;
24038             case 37: // left
24039             case 39: // right
24040                 this.onTogglePeriod();
24041                 break;
24042             case 38: // up
24043                 this.onIncrementMinutes();
24044                 break;
24045             case 40: // down
24046                 this.onDecrementMinutes();
24047                 break;
24048             case 13: // enter
24049             case 9: // tab
24050                 this.setTime();
24051                 break;
24052         }
24053     },
24054     
24055     onClick: function(e) {
24056         e.stopPropagation();
24057         e.preventDefault();
24058     },
24059     
24060     picker : function()
24061     {
24062         return this.pickerEl;
24063     },
24064     
24065     fillTime: function()
24066     {    
24067         var time = this.pop.select('tbody', true).first();
24068         
24069         time.dom.innerHTML = '';
24070         
24071         time.createChild({
24072             tag: 'tr',
24073             cn: [
24074                 {
24075                     tag: 'td',
24076                     cn: [
24077                         {
24078                             tag: 'a',
24079                             href: '#',
24080                             cls: 'btn',
24081                             cn: [
24082                                 {
24083                                     tag: 'i',
24084                                     cls: 'hours-up fa fas fa-chevron-up'
24085                                 }
24086                             ]
24087                         } 
24088                     ]
24089                 },
24090                 {
24091                     tag: 'td',
24092                     cls: 'separator'
24093                 },
24094                 {
24095                     tag: 'td',
24096                     cn: [
24097                         {
24098                             tag: 'a',
24099                             href: '#',
24100                             cls: 'btn',
24101                             cn: [
24102                                 {
24103                                     tag: 'i',
24104                                     cls: 'minutes-up fa fas fa-chevron-up'
24105                                 }
24106                             ]
24107                         }
24108                     ]
24109                 },
24110                 {
24111                     tag: 'td',
24112                     cls: 'separator'
24113                 }
24114             ]
24115         });
24116         
24117         time.createChild({
24118             tag: 'tr',
24119             cn: [
24120                 {
24121                     tag: 'td',
24122                     cn: [
24123                         {
24124                             tag: 'span',
24125                             cls: 'timepicker-hour',
24126                             html: '00'
24127                         }  
24128                     ]
24129                 },
24130                 {
24131                     tag: 'td',
24132                     cls: 'separator',
24133                     html: ':'
24134                 },
24135                 {
24136                     tag: 'td',
24137                     cn: [
24138                         {
24139                             tag: 'span',
24140                             cls: 'timepicker-minute',
24141                             html: '00'
24142                         }  
24143                     ]
24144                 },
24145                 {
24146                     tag: 'td',
24147                     cls: 'separator'
24148                 },
24149                 {
24150                     tag: 'td',
24151                     cn: [
24152                         {
24153                             tag: 'button',
24154                             type: 'button',
24155                             cls: 'btn btn-primary period',
24156                             html: 'AM'
24157                             
24158                         }
24159                     ]
24160                 }
24161             ]
24162         });
24163         
24164         time.createChild({
24165             tag: 'tr',
24166             cn: [
24167                 {
24168                     tag: 'td',
24169                     cn: [
24170                         {
24171                             tag: 'a',
24172                             href: '#',
24173                             cls: 'btn',
24174                             cn: [
24175                                 {
24176                                     tag: 'span',
24177                                     cls: 'hours-down fa fas fa-chevron-down'
24178                                 }
24179                             ]
24180                         }
24181                     ]
24182                 },
24183                 {
24184                     tag: 'td',
24185                     cls: 'separator'
24186                 },
24187                 {
24188                     tag: 'td',
24189                     cn: [
24190                         {
24191                             tag: 'a',
24192                             href: '#',
24193                             cls: 'btn',
24194                             cn: [
24195                                 {
24196                                     tag: 'span',
24197                                     cls: 'minutes-down fa fas fa-chevron-down'
24198                                 }
24199                             ]
24200                         }
24201                     ]
24202                 },
24203                 {
24204                     tag: 'td',
24205                     cls: 'separator'
24206                 }
24207             ]
24208         });
24209         
24210     },
24211     
24212     update: function()
24213     {
24214         
24215         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24216         
24217         this.fill();
24218     },
24219     
24220     fill: function() 
24221     {
24222         var hours = this.time.getHours();
24223         var minutes = this.time.getMinutes();
24224         var period = 'AM';
24225         
24226         if(hours > 11){
24227             period = 'PM';
24228         }
24229         
24230         if(hours == 0){
24231             hours = 12;
24232         }
24233         
24234         
24235         if(hours > 12){
24236             hours = hours - 12;
24237         }
24238         
24239         if(hours < 10){
24240             hours = '0' + hours;
24241         }
24242         
24243         if(minutes < 10){
24244             minutes = '0' + minutes;
24245         }
24246         
24247         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24248         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24249         this.pop.select('button', true).first().dom.innerHTML = period;
24250         
24251     },
24252     
24253     place: function()
24254     {   
24255         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24256         
24257         var cls = ['bottom'];
24258         
24259         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24260             cls.pop();
24261             cls.push('top');
24262         }
24263         
24264         cls.push('right');
24265         
24266         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24267             cls.pop();
24268             cls.push('left');
24269         }
24270         //this.picker().setXY(20000,20000);
24271         this.picker().addClass(cls.join('-'));
24272         
24273         var _this = this;
24274         
24275         Roo.each(cls, function(c){
24276             if(c == 'bottom'){
24277                 (function() {
24278                  //  
24279                 }).defer(200);
24280                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
24281                 //_this.picker().setTop(_this.inputEl().getHeight());
24282                 return;
24283             }
24284             if(c == 'top'){
24285                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
24286                 
24287                 //_this.picker().setTop(0 - _this.picker().getHeight());
24288                 return;
24289             }
24290             /*
24291             if(c == 'left'){
24292                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24293                 return;
24294             }
24295             if(c == 'right'){
24296                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24297                 return;
24298             }
24299             */
24300         });
24301         
24302     },
24303   
24304     onFocus : function()
24305     {
24306         Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24307         this.show();
24308     },
24309     
24310     onBlur : function()
24311     {
24312         Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24313         this.hide();
24314     },
24315     
24316     show : function()
24317     {
24318         this.picker().show();
24319         this.pop.show();
24320         this.update();
24321         this.place();
24322         
24323         this.fireEvent('show', this, this.date);
24324     },
24325     
24326     hide : function()
24327     {
24328         this.picker().hide();
24329         this.pop.hide();
24330         
24331         this.fireEvent('hide', this, this.date);
24332     },
24333     
24334     setTime : function()
24335     {
24336         this.hide();
24337         this.setValue(this.time.format(this.format));
24338         
24339         this.fireEvent('select', this, this.date);
24340         
24341         
24342     },
24343     
24344     onMousedown: function(e){
24345         e.stopPropagation();
24346         e.preventDefault();
24347     },
24348     
24349     onIncrementHours: function()
24350     {
24351         Roo.log('onIncrementHours');
24352         this.time = this.time.add(Date.HOUR, 1);
24353         this.update();
24354         
24355     },
24356     
24357     onDecrementHours: function()
24358     {
24359         Roo.log('onDecrementHours');
24360         this.time = this.time.add(Date.HOUR, -1);
24361         this.update();
24362     },
24363     
24364     onIncrementMinutes: function()
24365     {
24366         Roo.log('onIncrementMinutes');
24367         this.time = this.time.add(Date.MINUTE, 1);
24368         this.update();
24369     },
24370     
24371     onDecrementMinutes: function()
24372     {
24373         Roo.log('onDecrementMinutes');
24374         this.time = this.time.add(Date.MINUTE, -1);
24375         this.update();
24376     },
24377     
24378     onTogglePeriod: function()
24379     {
24380         Roo.log('onTogglePeriod');
24381         this.time = this.time.add(Date.HOUR, 12);
24382         this.update();
24383     }
24384     
24385    
24386 });
24387  
24388
24389 Roo.apply(Roo.bootstrap.form.TimeField,  {
24390   
24391     template : {
24392         tag: 'div',
24393         cls: 'datepicker dropdown-menu',
24394         cn: [
24395             {
24396                 tag: 'div',
24397                 cls: 'datepicker-time',
24398                 cn: [
24399                 {
24400                     tag: 'table',
24401                     cls: 'table-condensed',
24402                     cn:[
24403                         {
24404                             tag: 'tbody',
24405                             cn: [
24406                                 {
24407                                     tag: 'tr',
24408                                     cn: [
24409                                     {
24410                                         tag: 'td',
24411                                         colspan: '7'
24412                                     }
24413                                     ]
24414                                 }
24415                             ]
24416                         },
24417                         {
24418                             tag: 'tfoot',
24419                             cn: [
24420                                 {
24421                                     tag: 'tr',
24422                                     cn: [
24423                                     {
24424                                         tag: 'th',
24425                                         colspan: '7',
24426                                         cls: '',
24427                                         cn: [
24428                                             {
24429                                                 tag: 'button',
24430                                                 cls: 'btn btn-info ok',
24431                                                 html: 'OK'
24432                                             }
24433                                         ]
24434                                     }
24435                     
24436                                     ]
24437                                 }
24438                             ]
24439                         }
24440                     ]
24441                 }
24442                 ]
24443             }
24444         ]
24445     }
24446 });
24447
24448  
24449
24450  /*
24451  * - LGPL
24452  *
24453  * MonthField
24454  * 
24455  */
24456
24457 /**
24458  * @class Roo.bootstrap.form.MonthField
24459  * @extends Roo.bootstrap.form.Input
24460  * Bootstrap MonthField class
24461  * 
24462  * @cfg {String} language default en
24463  * 
24464  * @constructor
24465  * Create a new MonthField
24466  * @param {Object} config The config object
24467  */
24468
24469 Roo.bootstrap.form.MonthField = function(config){
24470     Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24471     
24472     this.addEvents({
24473         /**
24474          * @event show
24475          * Fires when this field show.
24476          * @param {Roo.bootstrap.form.MonthField} this
24477          * @param {Mixed} date The date value
24478          */
24479         show : true,
24480         /**
24481          * @event show
24482          * Fires when this field hide.
24483          * @param {Roo.bootstrap.form.MonthField} this
24484          * @param {Mixed} date The date value
24485          */
24486         hide : true,
24487         /**
24488          * @event select
24489          * Fires when select a date.
24490          * @param {Roo.bootstrap.form.MonthField} this
24491          * @param {String} oldvalue The old value
24492          * @param {String} newvalue The new value
24493          */
24494         select : true
24495     });
24496 };
24497
24498 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input,  {
24499     
24500     onRender: function(ct, position)
24501     {
24502         
24503         Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24504         
24505         this.language = this.language || 'en';
24506         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24507         this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24508         
24509         this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24510         this.isInline = false;
24511         this.isInput = true;
24512         this.component = this.el.select('.add-on', true).first() || false;
24513         this.component = (this.component && this.component.length === 0) ? false : this.component;
24514         this.hasInput = this.component && this.inputEL().length;
24515         
24516         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24517         
24518         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24519         
24520         this.picker().on('mousedown', this.onMousedown, this);
24521         this.picker().on('click', this.onClick, this);
24522         
24523         this.picker().addClass('datepicker-dropdown');
24524         
24525         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24526             v.setStyle('width', '189px');
24527         });
24528         
24529         this.fillMonths();
24530         
24531         this.update();
24532         
24533         if(this.isInline) {
24534             this.show();
24535         }
24536         
24537     },
24538     
24539     setValue: function(v, suppressEvent)
24540     {   
24541         var o = this.getValue();
24542         
24543         Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24544         
24545         this.update();
24546
24547         if(suppressEvent !== true){
24548             this.fireEvent('select', this, o, v);
24549         }
24550         
24551     },
24552     
24553     getValue: function()
24554     {
24555         return this.value;
24556     },
24557     
24558     onClick: function(e) 
24559     {
24560         e.stopPropagation();
24561         e.preventDefault();
24562         
24563         var target = e.getTarget();
24564         
24565         if(target.nodeName.toLowerCase() === 'i'){
24566             target = Roo.get(target).dom.parentNode;
24567         }
24568         
24569         var nodeName = target.nodeName;
24570         var className = target.className;
24571         var html = target.innerHTML;
24572         
24573         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24574             return;
24575         }
24576         
24577         this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24578         
24579         this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24580         
24581         this.hide();
24582                         
24583     },
24584     
24585     picker : function()
24586     {
24587         return this.pickerEl;
24588     },
24589     
24590     fillMonths: function()
24591     {    
24592         var i = 0;
24593         var months = this.picker().select('>.datepicker-months td', true).first();
24594         
24595         months.dom.innerHTML = '';
24596         
24597         while (i < 12) {
24598             var month = {
24599                 tag: 'span',
24600                 cls: 'month',
24601                 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24602             };
24603             
24604             months.createChild(month);
24605         }
24606         
24607     },
24608     
24609     update: function()
24610     {
24611         var _this = this;
24612         
24613         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24614             this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24615         }
24616         
24617         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24618             e.removeClass('active');
24619             
24620             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24621                 e.addClass('active');
24622             }
24623         })
24624     },
24625     
24626     place: function()
24627     {
24628         if(this.isInline) {
24629             return;
24630         }
24631         
24632         this.picker().removeClass(['bottom', 'top']);
24633         
24634         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24635             /*
24636              * place to the top of element!
24637              *
24638              */
24639             
24640             this.picker().addClass('top');
24641             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24642             
24643             return;
24644         }
24645         
24646         this.picker().addClass('bottom');
24647         
24648         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24649     },
24650     
24651     onFocus : function()
24652     {
24653         Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24654         this.show();
24655     },
24656     
24657     onBlur : function()
24658     {
24659         Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24660         
24661         var d = this.inputEl().getValue();
24662         
24663         this.setValue(d);
24664                 
24665         this.hide();
24666     },
24667     
24668     show : function()
24669     {
24670         this.picker().show();
24671         this.picker().select('>.datepicker-months', true).first().show();
24672         this.update();
24673         this.place();
24674         
24675         this.fireEvent('show', this, this.date);
24676     },
24677     
24678     hide : function()
24679     {
24680         if(this.isInline) {
24681             return;
24682         }
24683         this.picker().hide();
24684         this.fireEvent('hide', this, this.date);
24685         
24686     },
24687     
24688     onMousedown: function(e)
24689     {
24690         e.stopPropagation();
24691         e.preventDefault();
24692     },
24693     
24694     keyup: function(e)
24695     {
24696         Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24697         this.update();
24698     },
24699
24700     fireKey: function(e)
24701     {
24702         if (!this.picker().isVisible()){
24703             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24704                 this.show();
24705             }
24706             return;
24707         }
24708         
24709         var dir;
24710         
24711         switch(e.keyCode){
24712             case 27: // escape
24713                 this.hide();
24714                 e.preventDefault();
24715                 break;
24716             case 37: // left
24717             case 39: // right
24718                 dir = e.keyCode == 37 ? -1 : 1;
24719                 
24720                 this.vIndex = this.vIndex + dir;
24721                 
24722                 if(this.vIndex < 0){
24723                     this.vIndex = 0;
24724                 }
24725                 
24726                 if(this.vIndex > 11){
24727                     this.vIndex = 11;
24728                 }
24729                 
24730                 if(isNaN(this.vIndex)){
24731                     this.vIndex = 0;
24732                 }
24733                 
24734                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24735                 
24736                 break;
24737             case 38: // up
24738             case 40: // down
24739                 
24740                 dir = e.keyCode == 38 ? -1 : 1;
24741                 
24742                 this.vIndex = this.vIndex + dir * 4;
24743                 
24744                 if(this.vIndex < 0){
24745                     this.vIndex = 0;
24746                 }
24747                 
24748                 if(this.vIndex > 11){
24749                     this.vIndex = 11;
24750                 }
24751                 
24752                 if(isNaN(this.vIndex)){
24753                     this.vIndex = 0;
24754                 }
24755                 
24756                 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24757                 break;
24758                 
24759             case 13: // enter
24760                 
24761                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24762                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763                 }
24764                 
24765                 this.hide();
24766                 e.preventDefault();
24767                 break;
24768             case 9: // tab
24769                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24770                     this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24771                 }
24772                 this.hide();
24773                 break;
24774             case 16: // shift
24775             case 17: // ctrl
24776             case 18: // alt
24777                 break;
24778             default :
24779                 this.hide();
24780                 
24781         }
24782     },
24783     
24784     remove: function() 
24785     {
24786         this.picker().remove();
24787     }
24788    
24789 });
24790
24791 Roo.apply(Roo.bootstrap.form.MonthField,  {
24792     
24793     content : {
24794         tag: 'tbody',
24795         cn: [
24796         {
24797             tag: 'tr',
24798             cn: [
24799             {
24800                 tag: 'td',
24801                 colspan: '7'
24802             }
24803             ]
24804         }
24805         ]
24806     },
24807     
24808     dates:{
24809         en: {
24810             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24811             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24812         }
24813     }
24814 });
24815
24816 Roo.apply(Roo.bootstrap.form.MonthField,  {
24817   
24818     template : {
24819         tag: 'div',
24820         cls: 'datepicker dropdown-menu roo-dynamic',
24821         cn: [
24822             {
24823                 tag: 'div',
24824                 cls: 'datepicker-months',
24825                 cn: [
24826                 {
24827                     tag: 'table',
24828                     cls: 'table-condensed',
24829                     cn:[
24830                         Roo.bootstrap.form.DateField.content
24831                     ]
24832                 }
24833                 ]
24834             }
24835         ]
24836     }
24837 });
24838
24839  
24840
24841  
24842  /*
24843  * - LGPL
24844  *
24845  * CheckBox
24846  * 
24847  */
24848
24849 /**
24850  * @class Roo.bootstrap.form.CheckBox
24851  * @extends Roo.bootstrap.form.Input
24852  * Bootstrap CheckBox class
24853  * 
24854  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24855  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24856  * @cfg {String} boxLabel The text that appears beside the checkbox
24857  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24858  * @cfg {Boolean} checked initnal the element
24859  * @cfg {Boolean} inline inline the element (default false)
24860  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24861  * @cfg {String} tooltip label tooltip
24862  * 
24863  * @constructor
24864  * Create a new CheckBox
24865  * @param {Object} config The config object
24866  */
24867
24868 Roo.bootstrap.form.CheckBox = function(config){
24869     Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24870    
24871     this.addEvents({
24872         /**
24873         * @event check
24874         * Fires when the element is checked or unchecked.
24875         * @param {Roo.bootstrap.form.CheckBox} this This input
24876         * @param {Boolean} checked The new checked value
24877         */
24878        check : true,
24879        /**
24880         * @event click
24881         * Fires when the element is click.
24882         * @param {Roo.bootstrap.form.CheckBox} this This input
24883         */
24884        click : true
24885     });
24886     
24887 };
24888
24889 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input,  {
24890   
24891     inputType: 'checkbox',
24892     inputValue: 1,
24893     valueOff: 0,
24894     boxLabel: false,
24895     checked: false,
24896     weight : false,
24897     inline: false,
24898     tooltip : '',
24899     
24900     // checkbox success does not make any sense really.. 
24901     invalidClass : "",
24902     validClass : "",
24903     
24904     
24905     getAutoCreate : function()
24906     {
24907         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24908         
24909         var id = Roo.id();
24910         
24911         var cfg = {};
24912         
24913         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24914         
24915         if(this.inline){
24916             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24917         }
24918         
24919         var input =  {
24920             tag: 'input',
24921             id : id,
24922             type : this.inputType,
24923             value : this.inputValue,
24924             cls : 'roo-' + this.inputType, //'form-box',
24925             placeholder : this.placeholder || ''
24926             
24927         };
24928         
24929         if(this.inputType != 'radio'){
24930             var hidden =  {
24931                 tag: 'input',
24932                 type : 'hidden',
24933                 cls : 'roo-hidden-value',
24934                 value : this.checked ? this.inputValue : this.valueOff
24935             };
24936         }
24937         
24938             
24939         if (this.weight) { // Validity check?
24940             cfg.cls += " " + this.inputType + "-" + this.weight;
24941         }
24942         
24943         if (this.disabled) {
24944             input.disabled=true;
24945         }
24946         
24947         if(this.checked){
24948             input.checked = this.checked;
24949         }
24950         
24951         if (this.name) {
24952             
24953             input.name = this.name;
24954             
24955             if(this.inputType != 'radio'){
24956                 hidden.name = this.name;
24957                 input.name = '_hidden_' + this.name;
24958             }
24959         }
24960         
24961         if (this.size) {
24962             input.cls += ' input-' + this.size;
24963         }
24964         
24965         var settings=this;
24966         
24967         ['xs','sm','md','lg'].map(function(size){
24968             if (settings[size]) {
24969                 cfg.cls += ' col-' + size + '-' + settings[size];
24970             }
24971         });
24972         
24973         var inputblock = input;
24974          
24975         if (this.before || this.after) {
24976             
24977             inputblock = {
24978                 cls : 'input-group',
24979                 cn :  [] 
24980             };
24981             
24982             if (this.before) {
24983                 inputblock.cn.push({
24984                     tag :'span',
24985                     cls : 'input-group-addon',
24986                     html : this.before
24987                 });
24988             }
24989             
24990             inputblock.cn.push(input);
24991             
24992             if(this.inputType != 'radio'){
24993                 inputblock.cn.push(hidden);
24994             }
24995             
24996             if (this.after) {
24997                 inputblock.cn.push({
24998                     tag :'span',
24999                     cls : 'input-group-addon',
25000                     html : this.after
25001                 });
25002             }
25003             
25004         }
25005         var boxLabelCfg = false;
25006         
25007         if(this.boxLabel){
25008            
25009             boxLabelCfg = {
25010                 tag: 'label',
25011                 //'for': id, // box label is handled by onclick - so no for...
25012                 cls: 'box-label',
25013                 html: this.boxLabel
25014             };
25015             if(this.tooltip){
25016                 boxLabelCfg.tooltip = this.tooltip;
25017             }
25018              
25019         }
25020         
25021         
25022         if (align ==='left' && this.fieldLabel.length) {
25023 //                Roo.log("left and has label");
25024             cfg.cn = [
25025                 {
25026                     tag: 'label',
25027                     'for' :  id,
25028                     cls : 'control-label',
25029                     html : this.fieldLabel
25030                 },
25031                 {
25032                     cls : "", 
25033                     cn: [
25034                         inputblock
25035                     ]
25036                 }
25037             ];
25038             
25039             if (boxLabelCfg) {
25040                 cfg.cn[1].cn.push(boxLabelCfg);
25041             }
25042             
25043             if(this.labelWidth > 12){
25044                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25045             }
25046             
25047             if(this.labelWidth < 13 && this.labelmd == 0){
25048                 this.labelmd = this.labelWidth;
25049             }
25050             
25051             if(this.labellg > 0){
25052                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25053                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25054             }
25055             
25056             if(this.labelmd > 0){
25057                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25058                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25059             }
25060             
25061             if(this.labelsm > 0){
25062                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25063                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25064             }
25065             
25066             if(this.labelxs > 0){
25067                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25068                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25069             }
25070             
25071         } else if ( this.fieldLabel.length) {
25072 //                Roo.log(" label");
25073                 cfg.cn = [
25074                    
25075                     {
25076                         tag: this.boxLabel ? 'span' : 'label',
25077                         'for': id,
25078                         cls: 'control-label box-input-label',
25079                         //cls : 'input-group-addon',
25080                         html : this.fieldLabel
25081                     },
25082                     
25083                     inputblock
25084                     
25085                 ];
25086                 if (boxLabelCfg) {
25087                     cfg.cn.push(boxLabelCfg);
25088                 }
25089
25090         } else {
25091             
25092 //                Roo.log(" no label && no align");
25093                 cfg.cn = [  inputblock ] ;
25094                 if (boxLabelCfg) {
25095                     cfg.cn.push(boxLabelCfg);
25096                 }
25097
25098                 
25099         }
25100         
25101        
25102         
25103         if(this.inputType != 'radio'){
25104             cfg.cn.push(hidden);
25105         }
25106         
25107         return cfg;
25108         
25109     },
25110     
25111     /**
25112      * return the real input element.
25113      */
25114     inputEl: function ()
25115     {
25116         return this.el.select('input.roo-' + this.inputType,true).first();
25117     },
25118     hiddenEl: function ()
25119     {
25120         return this.el.select('input.roo-hidden-value',true).first();
25121     },
25122     
25123     labelEl: function()
25124     {
25125         return this.el.select('label.control-label',true).first();
25126     },
25127     /* depricated... */
25128     
25129     label: function()
25130     {
25131         return this.labelEl();
25132     },
25133     
25134     boxLabelEl: function()
25135     {
25136         return this.el.select('label.box-label',true).first();
25137     },
25138     
25139     initEvents : function()
25140     {
25141 //        Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25142         
25143         this.inputEl().on('click', this.onClick,  this);
25144         
25145         if (this.boxLabel) { 
25146             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
25147         }
25148         
25149         this.startValue = this.getValue();
25150         
25151         if(this.groupId){
25152             Roo.bootstrap.form.CheckBox.register(this);
25153         }
25154     },
25155     
25156     onClick : function(e)
25157     {   
25158         if(this.fireEvent('click', this, e) !== false){
25159             this.setChecked(!this.checked);
25160         }
25161         
25162     },
25163     
25164     setChecked : function(state,suppressEvent)
25165     {
25166         this.startValue = this.getValue();
25167
25168         if(this.inputType == 'radio'){
25169             
25170             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25171                 e.dom.checked = false;
25172             });
25173             
25174             this.inputEl().dom.checked = true;
25175             
25176             this.inputEl().dom.value = this.inputValue;
25177             
25178             if(suppressEvent !== true){
25179                 this.fireEvent('check', this, true);
25180             }
25181             
25182             this.validate();
25183             
25184             return;
25185         }
25186         
25187         this.checked = state;
25188         
25189         this.inputEl().dom.checked = state;
25190         
25191         
25192         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25193         
25194         if(suppressEvent !== true){
25195             this.fireEvent('check', this, state);
25196         }
25197         
25198         this.validate();
25199     },
25200     
25201     getValue : function()
25202     {
25203         if(this.inputType == 'radio'){
25204             return this.getGroupValue();
25205         }
25206         
25207         return this.hiddenEl().dom.value;
25208         
25209     },
25210     
25211     getGroupValue : function()
25212     {
25213         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25214             return '';
25215         }
25216         
25217         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25218     },
25219     
25220     setValue : function(v,suppressEvent)
25221     {
25222         if(this.inputType == 'radio'){
25223             this.setGroupValue(v, suppressEvent);
25224             return;
25225         }
25226         
25227         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25228         
25229         this.validate();
25230     },
25231     
25232     setGroupValue : function(v, suppressEvent)
25233     {
25234         this.startValue = this.getValue();
25235         
25236         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25237             e.dom.checked = false;
25238             
25239             if(e.dom.value == v){
25240                 e.dom.checked = true;
25241             }
25242         });
25243         
25244         if(suppressEvent !== true){
25245             this.fireEvent('check', this, true);
25246         }
25247
25248         this.validate();
25249         
25250         return;
25251     },
25252     
25253     validate : function()
25254     {
25255         if(this.getVisibilityEl().hasClass('hidden')){
25256             return true;
25257         }
25258         
25259         if(
25260                 this.disabled || 
25261                 (this.inputType == 'radio' && this.validateRadio()) ||
25262                 (this.inputType == 'checkbox' && this.validateCheckbox())
25263         ){
25264             this.markValid();
25265             return true;
25266         }
25267         
25268         this.markInvalid();
25269         return false;
25270     },
25271     
25272     validateRadio : function()
25273     {
25274         if(this.getVisibilityEl().hasClass('hidden')){
25275             return true;
25276         }
25277         
25278         if(this.allowBlank){
25279             return true;
25280         }
25281         
25282         var valid = false;
25283         
25284         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25285             if(!e.dom.checked){
25286                 return;
25287             }
25288             
25289             valid = true;
25290             
25291             return false;
25292         });
25293         
25294         return valid;
25295     },
25296     
25297     validateCheckbox : function()
25298     {
25299         if(!this.groupId){
25300             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25301             //return (this.getValue() == this.inputValue) ? true : false;
25302         }
25303         
25304         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25305         
25306         if(!group){
25307             return false;
25308         }
25309         
25310         var r = false;
25311         
25312         for(var i in group){
25313             if(group[i].el.isVisible(true)){
25314                 r = false;
25315                 break;
25316             }
25317             
25318             r = true;
25319         }
25320         
25321         for(var i in group){
25322             if(r){
25323                 break;
25324             }
25325             
25326             r = (group[i].getValue() == group[i].inputValue) ? true : false;
25327         }
25328         
25329         return r;
25330     },
25331     
25332     /**
25333      * Mark this field as valid
25334      */
25335     markValid : function()
25336     {
25337         var _this = this;
25338         
25339         this.fireEvent('valid', this);
25340         
25341         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25342         
25343         if(this.groupId){
25344             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25345         }
25346         
25347         if(label){
25348             label.markValid();
25349         }
25350
25351         if(this.inputType == 'radio'){
25352             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25353                 var fg = e.findParent('.form-group', false, true);
25354                 if (Roo.bootstrap.version == 3) {
25355                     fg.removeClass([_this.invalidClass, _this.validClass]);
25356                     fg.addClass(_this.validClass);
25357                 } else {
25358                     fg.removeClass(['is-valid', 'is-invalid']);
25359                     fg.addClass('is-valid');
25360                 }
25361             });
25362             
25363             return;
25364         }
25365
25366         if(!this.groupId){
25367             var fg = this.el.findParent('.form-group', false, true);
25368             if (Roo.bootstrap.version == 3) {
25369                 fg.removeClass([this.invalidClass, this.validClass]);
25370                 fg.addClass(this.validClass);
25371             } else {
25372                 fg.removeClass(['is-valid', 'is-invalid']);
25373                 fg.addClass('is-valid');
25374             }
25375             return;
25376         }
25377         
25378         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25379         
25380         if(!group){
25381             return;
25382         }
25383         
25384         for(var i in group){
25385             var fg = group[i].el.findParent('.form-group', false, true);
25386             if (Roo.bootstrap.version == 3) {
25387                 fg.removeClass([this.invalidClass, this.validClass]);
25388                 fg.addClass(this.validClass);
25389             } else {
25390                 fg.removeClass(['is-valid', 'is-invalid']);
25391                 fg.addClass('is-valid');
25392             }
25393         }
25394     },
25395     
25396      /**
25397      * Mark this field as invalid
25398      * @param {String} msg The validation message
25399      */
25400     markInvalid : function(msg)
25401     {
25402         if(this.allowBlank){
25403             return;
25404         }
25405         
25406         var _this = this;
25407         
25408         this.fireEvent('invalid', this, msg);
25409         
25410         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25411         
25412         if(this.groupId){
25413             label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25414         }
25415         
25416         if(label){
25417             label.markInvalid();
25418         }
25419             
25420         if(this.inputType == 'radio'){
25421             
25422             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25423                 var fg = e.findParent('.form-group', false, true);
25424                 if (Roo.bootstrap.version == 3) {
25425                     fg.removeClass([_this.invalidClass, _this.validClass]);
25426                     fg.addClass(_this.invalidClass);
25427                 } else {
25428                     fg.removeClass(['is-invalid', 'is-valid']);
25429                     fg.addClass('is-invalid');
25430                 }
25431             });
25432             
25433             return;
25434         }
25435         
25436         if(!this.groupId){
25437             var fg = this.el.findParent('.form-group', false, true);
25438             if (Roo.bootstrap.version == 3) {
25439                 fg.removeClass([_this.invalidClass, _this.validClass]);
25440                 fg.addClass(_this.invalidClass);
25441             } else {
25442                 fg.removeClass(['is-invalid', 'is-valid']);
25443                 fg.addClass('is-invalid');
25444             }
25445             return;
25446         }
25447         
25448         var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25449         
25450         if(!group){
25451             return;
25452         }
25453         
25454         for(var i in group){
25455             var fg = group[i].el.findParent('.form-group', false, true);
25456             if (Roo.bootstrap.version == 3) {
25457                 fg.removeClass([_this.invalidClass, _this.validClass]);
25458                 fg.addClass(_this.invalidClass);
25459             } else {
25460                 fg.removeClass(['is-invalid', 'is-valid']);
25461                 fg.addClass('is-invalid');
25462             }
25463         }
25464         
25465     },
25466     
25467     clearInvalid : function()
25468     {
25469         Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25470         
25471         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25472         
25473         var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25474         
25475         if (label && label.iconEl) {
25476             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25477             label.iconEl.removeClass(['is-invalid', 'is-valid']);
25478         }
25479     },
25480     
25481     disable : function()
25482     {
25483         if(this.inputType != 'radio'){
25484             Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25485             return;
25486         }
25487         
25488         var _this = this;
25489         
25490         if(this.rendered){
25491             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25492                 _this.getActionEl().addClass(this.disabledClass);
25493                 e.dom.disabled = true;
25494             });
25495         }
25496         
25497         this.disabled = true;
25498         this.fireEvent("disable", this);
25499         return this;
25500     },
25501
25502     enable : function()
25503     {
25504         if(this.inputType != 'radio'){
25505             Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25506             return;
25507         }
25508         
25509         var _this = this;
25510         
25511         if(this.rendered){
25512             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25513                 _this.getActionEl().removeClass(this.disabledClass);
25514                 e.dom.disabled = false;
25515             });
25516         }
25517         
25518         this.disabled = false;
25519         this.fireEvent("enable", this);
25520         return this;
25521     },
25522     
25523     setBoxLabel : function(v)
25524     {
25525         this.boxLabel = v;
25526         
25527         if(this.rendered){
25528             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25529         }
25530     }
25531
25532 });
25533
25534 Roo.apply(Roo.bootstrap.form.CheckBox, {
25535     
25536     groups: {},
25537     
25538      /**
25539     * register a CheckBox Group
25540     * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25541     */
25542     register : function(checkbox)
25543     {
25544         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25545             this.groups[checkbox.groupId] = {};
25546         }
25547         
25548         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25549             return;
25550         }
25551         
25552         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25553         
25554     },
25555     /**
25556     * fetch a CheckBox Group based on the group ID
25557     * @param {string} the group ID
25558     * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25559     */
25560     get: function(groupId) {
25561         if (typeof(this.groups[groupId]) == 'undefined') {
25562             return false;
25563         }
25564         
25565         return this.groups[groupId] ;
25566     }
25567     
25568     
25569 });
25570 /*
25571  * - LGPL
25572  *
25573  * RadioItem
25574  * 
25575  */
25576
25577 /**
25578  * @class Roo.bootstrap.form.Radio
25579  * @extends Roo.bootstrap.Component
25580  * Bootstrap Radio class
25581  * @cfg {String} boxLabel - the label associated
25582  * @cfg {String} value - the value of radio
25583  * 
25584  * @constructor
25585  * Create a new Radio
25586  * @param {Object} config The config object
25587  */
25588 Roo.bootstrap.form.Radio = function(config){
25589     Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25590     
25591 };
25592
25593 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25594     
25595     boxLabel : '',
25596     
25597     value : '',
25598     
25599     getAutoCreate : function()
25600     {
25601         var cfg = {
25602             tag : 'div',
25603             cls : 'form-group radio',
25604             cn : [
25605                 {
25606                     tag : 'label',
25607                     cls : 'box-label',
25608                     html : this.boxLabel
25609                 }
25610             ]
25611         };
25612         
25613         return cfg;
25614     },
25615     
25616     initEvents : function() 
25617     {
25618         this.parent().register(this);
25619         
25620         this.el.on('click', this.onClick, this);
25621         
25622     },
25623     
25624     onClick : function(e)
25625     {
25626         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25627             this.setChecked(true);
25628         }
25629     },
25630     
25631     setChecked : function(state, suppressEvent)
25632     {
25633         this.parent().setValue(this.value, suppressEvent);
25634         
25635     },
25636     
25637     setBoxLabel : function(v)
25638     {
25639         this.boxLabel = v;
25640         
25641         if(this.rendered){
25642             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25643         }
25644     }
25645     
25646 });
25647  
25648
25649  /*
25650  * - LGPL
25651  *
25652  * Input
25653  * 
25654  */
25655
25656 /**
25657  * @class Roo.bootstrap.form.SecurePass
25658  * @extends Roo.bootstrap.form.Input
25659  * Bootstrap SecurePass class
25660  *
25661  * 
25662  * @constructor
25663  * Create a new SecurePass
25664  * @param {Object} config The config object
25665  */
25666  
25667 Roo.bootstrap.form.SecurePass = function (config) {
25668     // these go here, so the translation tool can replace them..
25669     this.errors = {
25670         PwdEmpty: "Please type a password, and then retype it to confirm.",
25671         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25672         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25673         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25674         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25675         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25676         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25677         TooWeak: "Your password is Too Weak."
25678     },
25679     this.meterLabel = "Password strength:";
25680     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25681     this.meterClass = [
25682         "roo-password-meter-tooweak", 
25683         "roo-password-meter-weak", 
25684         "roo-password-meter-medium", 
25685         "roo-password-meter-strong", 
25686         "roo-password-meter-grey"
25687     ];
25688     
25689     this.errors = {};
25690     
25691     Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25692 }
25693
25694 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25695     /**
25696      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25697      * {
25698      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25699      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25700      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25701      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25702      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25703      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25704      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25705      * })
25706      */
25707     // private
25708     
25709     meterWidth: 300,
25710     errorMsg :'',    
25711     errors: false,
25712     imageRoot: '/',
25713     /**
25714      * @cfg {String/Object} Label for the strength meter (defaults to
25715      * 'Password strength:')
25716      */
25717     // private
25718     meterLabel: '',
25719     /**
25720      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25721      * ['Weak', 'Medium', 'Strong'])
25722      */
25723     // private    
25724     pwdStrengths: false,    
25725     // private
25726     strength: 0,
25727     // private
25728     _lastPwd: null,
25729     // private
25730     kCapitalLetter: 0,
25731     kSmallLetter: 1,
25732     kDigit: 2,
25733     kPunctuation: 3,
25734     
25735     insecure: false,
25736     // private
25737     initEvents: function ()
25738     {
25739         Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25740
25741         if (this.el.is('input[type=password]') && Roo.isSafari) {
25742             this.el.on('keydown', this.SafariOnKeyDown, this);
25743         }
25744
25745         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25746     },
25747     // private
25748     onRender: function (ct, position)
25749     {
25750         Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25751         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25752         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25753
25754         this.trigger.createChild({
25755                    cn: [
25756                     {
25757                     //id: 'PwdMeter',
25758                     tag: 'div',
25759                     cls: 'roo-password-meter-grey col-xs-12',
25760                     style: {
25761                         //width: 0,
25762                         //width: this.meterWidth + 'px'                                                
25763                         }
25764                     },
25765                     {                            
25766                          cls: 'roo-password-meter-text'                          
25767                     }
25768                 ]            
25769         });
25770
25771          
25772         if (this.hideTrigger) {
25773             this.trigger.setDisplayed(false);
25774         }
25775         this.setSize(this.width || '', this.height || '');
25776     },
25777     // private
25778     onDestroy: function ()
25779     {
25780         if (this.trigger) {
25781             this.trigger.removeAllListeners();
25782             this.trigger.remove();
25783         }
25784         if (this.wrap) {
25785             this.wrap.remove();
25786         }
25787         Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25788     },
25789     // private
25790     checkStrength: function ()
25791     {
25792         var pwd = this.inputEl().getValue();
25793         if (pwd == this._lastPwd) {
25794             return;
25795         }
25796
25797         var strength;
25798         if (this.ClientSideStrongPassword(pwd)) {
25799             strength = 3;
25800         } else if (this.ClientSideMediumPassword(pwd)) {
25801             strength = 2;
25802         } else if (this.ClientSideWeakPassword(pwd)) {
25803             strength = 1;
25804         } else {
25805             strength = 0;
25806         }
25807         
25808         Roo.log('strength1: ' + strength);
25809         
25810         //var pm = this.trigger.child('div/div/div').dom;
25811         var pm = this.trigger.child('div/div');
25812         pm.removeClass(this.meterClass);
25813         pm.addClass(this.meterClass[strength]);
25814                 
25815         
25816         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25817                 
25818         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25819         
25820         this._lastPwd = pwd;
25821     },
25822     reset: function ()
25823     {
25824         Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25825         
25826         this._lastPwd = '';
25827         
25828         var pm = this.trigger.child('div/div');
25829         pm.removeClass(this.meterClass);
25830         pm.addClass('roo-password-meter-grey');        
25831         
25832         
25833         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25834         
25835         pt.innerHTML = '';
25836         this.inputEl().dom.type='password';
25837     },
25838     // private
25839     validateValue: function (value)
25840     {
25841         if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25842             return false;
25843         }
25844         if (value.length == 0) {
25845             if (this.allowBlank) {
25846                 this.clearInvalid();
25847                 return true;
25848             }
25849
25850             this.markInvalid(this.errors.PwdEmpty);
25851             this.errorMsg = this.errors.PwdEmpty;
25852             return false;
25853         }
25854         
25855         if(this.insecure){
25856             return true;
25857         }
25858         
25859         if (!value.match(/[\x21-\x7e]+/)) {
25860             this.markInvalid(this.errors.PwdBadChar);
25861             this.errorMsg = this.errors.PwdBadChar;
25862             return false;
25863         }
25864         if (value.length < 6) {
25865             this.markInvalid(this.errors.PwdShort);
25866             this.errorMsg = this.errors.PwdShort;
25867             return false;
25868         }
25869         if (value.length > 16) {
25870             this.markInvalid(this.errors.PwdLong);
25871             this.errorMsg = this.errors.PwdLong;
25872             return false;
25873         }
25874         var strength;
25875         if (this.ClientSideStrongPassword(value)) {
25876             strength = 3;
25877         } else if (this.ClientSideMediumPassword(value)) {
25878             strength = 2;
25879         } else if (this.ClientSideWeakPassword(value)) {
25880             strength = 1;
25881         } else {
25882             strength = 0;
25883         }
25884
25885         
25886         if (strength < 2) {
25887             //this.markInvalid(this.errors.TooWeak);
25888             this.errorMsg = this.errors.TooWeak;
25889             //return false;
25890         }
25891         
25892         
25893         console.log('strength2: ' + strength);
25894         
25895         //var pm = this.trigger.child('div/div/div').dom;
25896         
25897         var pm = this.trigger.child('div/div');
25898         pm.removeClass(this.meterClass);
25899         pm.addClass(this.meterClass[strength]);
25900                 
25901         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25902                 
25903         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25904         
25905         this.errorMsg = ''; 
25906         return true;
25907     },
25908     // private
25909     CharacterSetChecks: function (type)
25910     {
25911         this.type = type;
25912         this.fResult = false;
25913     },
25914     // private
25915     isctype: function (character, type)
25916     {
25917         switch (type) {  
25918             case this.kCapitalLetter:
25919                 if (character >= 'A' && character <= 'Z') {
25920                     return true;
25921                 }
25922                 break;
25923             
25924             case this.kSmallLetter:
25925                 if (character >= 'a' && character <= 'z') {
25926                     return true;
25927                 }
25928                 break;
25929             
25930             case this.kDigit:
25931                 if (character >= '0' && character <= '9') {
25932                     return true;
25933                 }
25934                 break;
25935             
25936             case this.kPunctuation:
25937                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25938                     return true;
25939                 }
25940                 break;
25941             
25942             default:
25943                 return false;
25944         }
25945
25946     },
25947     // private
25948     IsLongEnough: function (pwd, size)
25949     {
25950         return !(pwd == null || isNaN(size) || pwd.length < size);
25951     },
25952     // private
25953     SpansEnoughCharacterSets: function (word, nb)
25954     {
25955         if (!this.IsLongEnough(word, nb))
25956         {
25957             return false;
25958         }
25959
25960         var characterSetChecks = new Array(
25961             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25962             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25963         );
25964         
25965         for (var index = 0; index < word.length; ++index) {
25966             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25967                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25968                     characterSetChecks[nCharSet].fResult = true;
25969                     break;
25970                 }
25971             }
25972         }
25973
25974         var nCharSets = 0;
25975         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25976             if (characterSetChecks[nCharSet].fResult) {
25977                 ++nCharSets;
25978             }
25979         }
25980
25981         if (nCharSets < nb) {
25982             return false;
25983         }
25984         return true;
25985     },
25986     // private
25987     ClientSideStrongPassword: function (pwd)
25988     {
25989         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25990     },
25991     // private
25992     ClientSideMediumPassword: function (pwd)
25993     {
25994         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25995     },
25996     // private
25997     ClientSideWeakPassword: function (pwd)
25998     {
25999         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26000     }
26001           
26002 });
26003 Roo.htmleditor = {};
26004  
26005 /**
26006  * @class Roo.htmleditor.Filter
26007  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26008  * @cfg {DomElement} node The node to iterate and filter
26009  * @cfg {boolean|String|Array} tag Tags to replace 
26010  * @constructor
26011  * Create a new Filter.
26012  * @param {Object} config Configuration options
26013  */
26014
26015
26016
26017 Roo.htmleditor.Filter = function(cfg) {
26018     Roo.apply(this.cfg);
26019     // this does not actually call walk as it's really just a abstract class
26020 }
26021
26022
26023 Roo.htmleditor.Filter.prototype = {
26024     
26025     node: false,
26026     
26027     tag: false,
26028
26029     // overrride to do replace comments.
26030     replaceComment : false,
26031     
26032     // overrride to do replace or do stuff with tags..
26033     replaceTag : false,
26034     
26035     walk : function(dom)
26036     {
26037         Roo.each( Array.from(dom.childNodes), function( e ) {
26038             switch(true) {
26039                 
26040                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
26041                     this.replaceComment(e);
26042                     return;
26043                 
26044                 case e.nodeType != 1: //not a node.
26045                     return;
26046                 
26047                 case this.tag === true: // everything
26048                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26049                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26050                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26051                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26052                     if (this.replaceTag && false === this.replaceTag(e)) {
26053                         return;
26054                     }
26055                     if (e.hasChildNodes()) {
26056                         this.walk(e);
26057                     }
26058                     return;
26059                 
26060                 default:    // tags .. that do not match.
26061                     if (e.hasChildNodes()) {
26062                         this.walk(e);
26063                     }
26064             }
26065             
26066         }, this);
26067         
26068     },
26069     
26070     
26071     removeNodeKeepChildren : function( node)
26072     {
26073     
26074         ar = Array.from(node.childNodes);
26075         for (var i = 0; i < ar.length; i++) {
26076          
26077             node.removeChild(ar[i]);
26078             // what if we need to walk these???
26079             node.parentNode.insertBefore(ar[i], node);
26080            
26081         }
26082         node.parentNode.removeChild(node);
26083     }
26084 }; 
26085
26086 /**
26087  * @class Roo.htmleditor.FilterAttributes
26088  * clean attributes and  styles including http:// etc.. in attribute
26089  * @constructor
26090 * Run a new Attribute Filter
26091 * @param {Object} config Configuration options
26092  */
26093 Roo.htmleditor.FilterAttributes = function(cfg)
26094 {
26095     Roo.apply(this, cfg);
26096     this.attrib_black = this.attrib_black || [];
26097     this.attrib_white = this.attrib_white || [];
26098
26099     this.attrib_clean = this.attrib_clean || [];
26100     this.style_white = this.style_white || [];
26101     this.style_black = this.style_black || [];
26102     this.walk(cfg.node);
26103 }
26104
26105 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26106 {
26107     tag: true, // all tags
26108     
26109     attrib_black : false, // array
26110     attrib_clean : false,
26111     attrib_white : false,
26112
26113     style_white : false,
26114     style_black : false,
26115      
26116      
26117     replaceTag : function(node)
26118     {
26119         if (!node.attributes || !node.attributes.length) {
26120             return true;
26121         }
26122         
26123         for (var i = node.attributes.length-1; i > -1 ; i--) {
26124             var a = node.attributes[i];
26125             //console.log(a);
26126             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26127                 node.removeAttribute(a.name);
26128                 continue;
26129             }
26130             
26131             
26132             
26133             if (a.name.toLowerCase().substr(0,2)=='on')  {
26134                 node.removeAttribute(a.name);
26135                 continue;
26136             }
26137             
26138             
26139             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26140                 node.removeAttribute(a.name);
26141                 continue;
26142             }
26143             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26144                 this.cleanAttr(node,a.name,a.value); // fixme..
26145                 continue;
26146             }
26147             if (a.name == 'style') {
26148                 this.cleanStyle(node,a.name,a.value);
26149                 continue;
26150             }
26151             /// clean up MS crap..
26152             // tecnically this should be a list of valid class'es..
26153             
26154             
26155             if (a.name == 'class') {
26156                 if (a.value.match(/^Mso/)) {
26157                     node.removeAttribute('class');
26158                 }
26159                 
26160                 if (a.value.match(/^body$/)) {
26161                     node.removeAttribute('class');
26162                 }
26163                 continue;
26164             }
26165             
26166             
26167             // style cleanup!?
26168             // class cleanup?
26169             
26170         }
26171         return true; // clean children
26172     },
26173         
26174     cleanAttr: function(node, n,v)
26175     {
26176         
26177         if (v.match(/^\./) || v.match(/^\//)) {
26178             return;
26179         }
26180         if (v.match(/^(http|https):\/\//)
26181             || v.match(/^mailto:/) 
26182             || v.match(/^ftp:/)
26183             || v.match(/^data:/)
26184             ) {
26185             return;
26186         }
26187         if (v.match(/^#/)) {
26188             return;
26189         }
26190         if (v.match(/^\{/)) { // allow template editing.
26191             return;
26192         }
26193 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26194         node.removeAttribute(n);
26195         
26196     },
26197     cleanStyle : function(node,  n,v)
26198     {
26199         if (v.match(/expression/)) { //XSS?? should we even bother..
26200             node.removeAttribute(n);
26201             return;
26202         }
26203         
26204         var parts = v.split(/;/);
26205         var clean = [];
26206         
26207         Roo.each(parts, function(p) {
26208             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26209             if (!p.length) {
26210                 return true;
26211             }
26212             var l = p.split(':').shift().replace(/\s+/g,'');
26213             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26214             
26215             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26216                 return true;
26217             }
26218             //Roo.log()
26219             // only allow 'c whitelisted system attributes'
26220             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26221                 return true;
26222             }
26223             
26224             
26225             clean.push(p);
26226             return true;
26227         },this);
26228         if (clean.length) { 
26229             node.setAttribute(n, clean.join(';'));
26230         } else {
26231             node.removeAttribute(n);
26232         }
26233         
26234     }
26235         
26236         
26237         
26238     
26239 });/**
26240  * @class Roo.htmleditor.FilterBlack
26241  * remove blacklisted elements.
26242  * @constructor
26243  * Run a new Blacklisted Filter
26244  * @param {Object} config Configuration options
26245  */
26246
26247 Roo.htmleditor.FilterBlack = function(cfg)
26248 {
26249     Roo.apply(this, cfg);
26250     this.walk(cfg.node);
26251 }
26252
26253 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26254 {
26255     tag : true, // all elements.
26256    
26257     replaceTag : function(n)
26258     {
26259         n.parentNode.removeChild(n);
26260     }
26261 });
26262 /**
26263  * @class Roo.htmleditor.FilterComment
26264  * remove comments.
26265  * @constructor
26266 * Run a new Comments Filter
26267 * @param {Object} config Configuration options
26268  */
26269 Roo.htmleditor.FilterComment = function(cfg)
26270 {
26271     this.walk(cfg.node);
26272 }
26273
26274 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26275 {
26276   
26277     replaceComment : function(n)
26278     {
26279         n.parentNode.removeChild(n);
26280     }
26281 });/**
26282  * @class Roo.htmleditor.FilterKeepChildren
26283  * remove tags but keep children
26284  * @constructor
26285  * Run a new Keep Children Filter
26286  * @param {Object} config Configuration options
26287  */
26288
26289 Roo.htmleditor.FilterKeepChildren = function(cfg)
26290 {
26291     Roo.apply(this, cfg);
26292     if (this.tag === false) {
26293         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26294     }
26295     // hacky?
26296     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26297         this.cleanNamespace = true;
26298     }
26299         
26300     this.walk(cfg.node);
26301 }
26302
26303 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26304 {
26305     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26306   
26307     replaceTag : function(node)
26308     {
26309         // walk children...
26310         //Roo.log(node.tagName);
26311         var ar = Array.from(node.childNodes);
26312         //remove first..
26313         
26314         for (var i = 0; i < ar.length; i++) {
26315             var e = ar[i];
26316             if (e.nodeType == 1) {
26317                 if (
26318                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26319                     || // array and it matches
26320                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
26321                     ||
26322                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26323                     ||
26324                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26325                 ) {
26326                     this.replaceTag(ar[i]); // child is blacklisted as well...
26327                     continue;
26328                 }
26329             }
26330         }  
26331         ar = Array.from(node.childNodes);
26332         for (var i = 0; i < ar.length; i++) {
26333          
26334             node.removeChild(ar[i]);
26335             // what if we need to walk these???
26336             node.parentNode.insertBefore(ar[i], node);
26337             if (this.tag !== false) {
26338                 this.walk(ar[i]);
26339                 
26340             }
26341         }
26342         //Roo.log("REMOVE:" + node.tagName);
26343         node.parentNode.removeChild(node);
26344         return false; // don't walk children
26345         
26346         
26347     }
26348 });/**
26349  * @class Roo.htmleditor.FilterParagraph
26350  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26351  * like on 'push' to remove the <p> tags and replace them with line breaks.
26352  * @constructor
26353  * Run a new Paragraph Filter
26354  * @param {Object} config Configuration options
26355  */
26356
26357 Roo.htmleditor.FilterParagraph = function(cfg)
26358 {
26359     // no need to apply config.
26360     this.walk(cfg.node);
26361 }
26362
26363 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26364 {
26365     
26366      
26367     tag : 'P',
26368     
26369      
26370     replaceTag : function(node)
26371     {
26372         
26373         if (node.childNodes.length == 1 &&
26374             node.childNodes[0].nodeType == 3 &&
26375             node.childNodes[0].textContent.trim().length < 1
26376             ) {
26377             // remove and replace with '<BR>';
26378             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26379             return false; // no need to walk..
26380         }
26381         var ar = Array.from(node.childNodes);
26382         for (var i = 0; i < ar.length; i++) {
26383             node.removeChild(ar[i]);
26384             // what if we need to walk these???
26385             node.parentNode.insertBefore(ar[i], node);
26386         }
26387         // now what about this?
26388         // <p> &nbsp; </p>
26389         
26390         // double BR.
26391         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26392         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26393         node.parentNode.removeChild(node);
26394         
26395         return false;
26396
26397     }
26398     
26399 });/**
26400  * @class Roo.htmleditor.FilterSpan
26401  * filter span's with no attributes out..
26402  * @constructor
26403  * Run a new Span Filter
26404  * @param {Object} config Configuration options
26405  */
26406
26407 Roo.htmleditor.FilterSpan = function(cfg)
26408 {
26409     // no need to apply config.
26410     this.walk(cfg.node);
26411 }
26412
26413 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26414 {
26415      
26416     tag : 'SPAN',
26417      
26418  
26419     replaceTag : function(node)
26420     {
26421         if (node.attributes && node.attributes.length > 0) {
26422             return true; // walk if there are any.
26423         }
26424         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26425         return false;
26426      
26427     }
26428     
26429 });/**
26430  * @class Roo.htmleditor.FilterTableWidth
26431   try and remove table width data - as that frequently messes up other stuff.
26432  * 
26433  *      was cleanTableWidths.
26434  *
26435  * Quite often pasting from word etc.. results in tables with column and widths.
26436  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26437  *
26438  * @constructor
26439  * Run a new Table Filter
26440  * @param {Object} config Configuration options
26441  */
26442
26443 Roo.htmleditor.FilterTableWidth = function(cfg)
26444 {
26445     // no need to apply config.
26446     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26447     this.walk(cfg.node);
26448 }
26449
26450 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26451 {
26452      
26453      
26454     
26455     replaceTag: function(node) {
26456         
26457         
26458       
26459         if (node.hasAttribute('width')) {
26460             node.removeAttribute('width');
26461         }
26462         
26463          
26464         if (node.hasAttribute("style")) {
26465             // pretty basic...
26466             
26467             var styles = node.getAttribute("style").split(";");
26468             var nstyle = [];
26469             Roo.each(styles, function(s) {
26470                 if (!s.match(/:/)) {
26471                     return;
26472                 }
26473                 var kv = s.split(":");
26474                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26475                     return;
26476                 }
26477                 // what ever is left... we allow.
26478                 nstyle.push(s);
26479             });
26480             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26481             if (!nstyle.length) {
26482                 node.removeAttribute('style');
26483             }
26484         }
26485         
26486         return true; // continue doing children..
26487     }
26488 });/**
26489  * @class Roo.htmleditor.FilterWord
26490  * try and clean up all the mess that Word generates.
26491  * 
26492  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
26493  
26494  * @constructor
26495  * Run a new Span Filter
26496  * @param {Object} config Configuration options
26497  */
26498
26499 Roo.htmleditor.FilterWord = function(cfg)
26500 {
26501     // no need to apply config.
26502     this.replaceDocBullets(cfg.node);
26503     
26504     this.replaceAname(cfg.node);
26505     // this is disabled as the removal is done by other filters;
26506    // this.walk(cfg.node);
26507     
26508     
26509 }
26510
26511 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26512 {
26513     tag: true,
26514      
26515     
26516     /**
26517      * Clean up MS wordisms...
26518      */
26519     replaceTag : function(node)
26520     {
26521          
26522         // no idea what this does - span with text, replaceds with just text.
26523         if(
26524                 node.nodeName == 'SPAN' &&
26525                 !node.hasAttributes() &&
26526                 node.childNodes.length == 1 &&
26527                 node.firstChild.nodeName == "#text"  
26528         ) {
26529             var textNode = node.firstChild;
26530             node.removeChild(textNode);
26531             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26532                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26533             }
26534             node.parentNode.insertBefore(textNode, node);
26535             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26536                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26537             }
26538             
26539             node.parentNode.removeChild(node);
26540             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26541         }
26542         
26543    
26544         
26545         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26546             node.parentNode.removeChild(node);
26547             return false; // dont do chidlren
26548         }
26549         //Roo.log(node.tagName);
26550         // remove - but keep children..
26551         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26552             //Roo.log('-- removed');
26553             while (node.childNodes.length) {
26554                 var cn = node.childNodes[0];
26555                 node.removeChild(cn);
26556                 node.parentNode.insertBefore(cn, node);
26557                 // move node to parent - and clean it..
26558                 if (cn.nodeType == 1) {
26559                     this.replaceTag(cn);
26560                 }
26561                 
26562             }
26563             node.parentNode.removeChild(node);
26564             /// no need to iterate chidlren = it's got none..
26565             //this.iterateChildren(node, this.cleanWord);
26566             return false; // no need to iterate children.
26567         }
26568         // clean styles
26569         if (node.className.length) {
26570             
26571             var cn = node.className.split(/\W+/);
26572             var cna = [];
26573             Roo.each(cn, function(cls) {
26574                 if (cls.match(/Mso[a-zA-Z]+/)) {
26575                     return;
26576                 }
26577                 cna.push(cls);
26578             });
26579             node.className = cna.length ? cna.join(' ') : '';
26580             if (!cna.length) {
26581                 node.removeAttribute("class");
26582             }
26583         }
26584         
26585         if (node.hasAttribute("lang")) {
26586             node.removeAttribute("lang");
26587         }
26588         
26589         if (node.hasAttribute("style")) {
26590             
26591             var styles = node.getAttribute("style").split(";");
26592             var nstyle = [];
26593             Roo.each(styles, function(s) {
26594                 if (!s.match(/:/)) {
26595                     return;
26596                 }
26597                 var kv = s.split(":");
26598                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26599                     return;
26600                 }
26601                 // what ever is left... we allow.
26602                 nstyle.push(s);
26603             });
26604             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26605             if (!nstyle.length) {
26606                 node.removeAttribute('style');
26607             }
26608         }
26609         return true; // do children
26610         
26611         
26612         
26613     },
26614     
26615     styleToObject: function(node)
26616     {
26617         var styles = (node.getAttribute("style") || '').split(";");
26618         var ret = {};
26619         Roo.each(styles, function(s) {
26620             if (!s.match(/:/)) {
26621                 return;
26622             }
26623             var kv = s.split(":");
26624              
26625             // what ever is left... we allow.
26626             ret[kv[0].trim()] = kv[1];
26627         });
26628         return ret;
26629     },
26630     
26631     
26632     replaceAname : function (doc)
26633     {
26634         // replace all the a/name without..
26635         var aa = Array.from(doc.getElementsByTagName('a'));
26636         for (var i = 0; i  < aa.length; i++) {
26637             var a = aa[i];
26638             if (a.hasAttribute("name")) {
26639                 a.removeAttribute("name");
26640             }
26641             if (a.hasAttribute("href")) {
26642                 continue;
26643             }
26644             // reparent children.
26645             this.removeNodeKeepChildren(a);
26646             
26647         }
26648         
26649         
26650         
26651     },
26652
26653     
26654     
26655     replaceDocBullets : function(doc)
26656     {
26657         // this is a bit odd - but it appears some indents use ql-indent-1
26658          //Roo.log(doc.innerHTML);
26659         
26660         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26661         for( var i = 0; i < listpara.length; i ++) {
26662             listpara[i].className = "MsoListParagraph";
26663         }
26664         
26665         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26666         for( var i = 0; i < listpara.length; i ++) {
26667             listpara[i].className = "MsoListParagraph";
26668         }
26669         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26670         for( var i = 0; i < listpara.length; i ++) {
26671             listpara[i].className = "MsoListParagraph";
26672         }
26673         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
26674         for( var i = 0; i < listpara.length; i ++) {
26675             listpara[i].className = "MsoListParagraph";
26676         }
26677         
26678         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26679         var htwo =  Array.from(doc.getElementsByTagName('h2'));
26680         for( var i = 0; i < htwo.length; i ++) {
26681             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26682                 htwo[i].className = "MsoListParagraph";
26683             }
26684         }
26685         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
26686         for( var i = 0; i < listpara.length; i ++) {
26687             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26688                 listpara[i].className = "MsoListParagraph";
26689             } else {
26690                 listpara[i].className = "MsoNormalx";
26691             }
26692         }
26693        
26694         listpara = doc.getElementsByClassName('MsoListParagraph');
26695         // Roo.log(doc.innerHTML);
26696         
26697         
26698         
26699         while(listpara.length) {
26700             
26701             this.replaceDocBullet(listpara.item(0));
26702         }
26703       
26704     },
26705     
26706      
26707     
26708     replaceDocBullet : function(p)
26709     {
26710         // gather all the siblings.
26711         var ns = p,
26712             parent = p.parentNode,
26713             doc = parent.ownerDocument,
26714             items = [];
26715             
26716         var listtype = 'ul';   
26717         while (ns) {
26718             if (ns.nodeType != 1) {
26719                 ns = ns.nextSibling;
26720                 continue;
26721             }
26722             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26723                 break;
26724             }
26725             var spans = ns.getElementsByTagName('span');
26726             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26727                 items.push(ns);
26728                 ns = ns.nextSibling;
26729                 has_list = true;
26730                 if (spans.length && spans[0].hasAttribute('style')) {
26731                     var  style = this.styleToObject(spans[0]);
26732                     if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26733                         listtype = 'ol';
26734                     }
26735                 }
26736                 
26737                 continue;
26738             }
26739             var spans = ns.getElementsByTagName('span');
26740             if (!spans.length) {
26741                 break;
26742             }
26743             var has_list  = false;
26744             for(var i = 0; i < spans.length; i++) {
26745                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26746                     has_list = true;
26747                     break;
26748                 }
26749             }
26750             if (!has_list) {
26751                 break;
26752             }
26753             items.push(ns);
26754             ns = ns.nextSibling;
26755             
26756             
26757         }
26758         if (!items.length) {
26759             ns.className = "";
26760             return;
26761         }
26762         
26763         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26764         parent.insertBefore(ul, p);
26765         var lvl = 0;
26766         var stack = [ ul ];
26767         var last_li = false;
26768         
26769         var margin_to_depth = {};
26770         max_margins = -1;
26771         
26772         items.forEach(function(n, ipos) {
26773             //Roo.log("got innertHMLT=" + n.innerHTML);
26774             
26775             var spans = n.getElementsByTagName('span');
26776             if (!spans.length) {
26777                 //Roo.log("No spans found");
26778                  
26779                 parent.removeChild(n);
26780                 
26781                 
26782                 return; // skip it...
26783             }
26784            
26785                 
26786             var num = 1;
26787             var style = {};
26788             for(var i = 0; i < spans.length; i++) {
26789             
26790                 style = this.styleToObject(spans[i]);
26791                 if (typeof(style['mso-list']) == 'undefined') {
26792                     continue;
26793                 }
26794                 if (listtype == 'ol') {
26795                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
26796                 }
26797                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26798                 break;
26799             }
26800             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26801             style = this.styleToObject(n); // mo-list is from the parent node.
26802             if (typeof(style['mso-list']) == 'undefined') {
26803                 //Roo.log("parent is missing level");
26804                   
26805                 parent.removeChild(n);
26806                  
26807                 return;
26808             }
26809             
26810             var margin = style['margin-left'];
26811             if (typeof(margin_to_depth[margin]) == 'undefined') {
26812                 max_margins++;
26813                 margin_to_depth[margin] = max_margins;
26814             }
26815             nlvl = margin_to_depth[margin] ;
26816              
26817             if (nlvl > lvl) {
26818                 //new indent
26819                 var nul = doc.createElement(listtype); // what about number lists...
26820                 if (!last_li) {
26821                     last_li = doc.createElement('li');
26822                     stack[lvl].appendChild(last_li);
26823                 }
26824                 last_li.appendChild(nul);
26825                 stack[nlvl] = nul;
26826                 
26827             }
26828             lvl = nlvl;
26829             
26830             // not starting at 1..
26831             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26832                 stack[nlvl].setAttribute("start", num);
26833             }
26834             
26835             var nli = stack[nlvl].appendChild(doc.createElement('li'));
26836             last_li = nli;
26837             nli.innerHTML = n.innerHTML;
26838             //Roo.log("innerHTML = " + n.innerHTML);
26839             parent.removeChild(n);
26840             
26841              
26842              
26843             
26844         },this);
26845         
26846         
26847         
26848         
26849     }
26850     
26851     
26852     
26853 });
26854 /**
26855  * @class Roo.htmleditor.FilterStyleToTag
26856  * part of the word stuff... - certain 'styles' should be converted to tags.
26857  * eg.
26858  *   font-weight: bold -> bold
26859  *   ?? super / subscrit etc..
26860  * 
26861  * @constructor
26862 * Run a new style to tag filter.
26863 * @param {Object} config Configuration options
26864  */
26865 Roo.htmleditor.FilterStyleToTag = function(cfg)
26866 {
26867     
26868     this.tags = {
26869         B  : [ 'fontWeight' , 'bold'],
26870         I :  [ 'fontStyle' , 'italic'],
26871         //pre :  [ 'font-style' , 'italic'],
26872         // h1.. h6 ?? font-size?
26873         SUP : [ 'verticalAlign' , 'super' ],
26874         SUB : [ 'verticalAlign' , 'sub' ]
26875         
26876         
26877     };
26878     
26879     Roo.apply(this, cfg);
26880      
26881     
26882     this.walk(cfg.node);
26883     
26884     
26885     
26886 }
26887
26888
26889 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26890 {
26891     tag: true, // all tags
26892     
26893     tags : false,
26894     
26895     
26896     replaceTag : function(node)
26897     {
26898         
26899         
26900         if (node.getAttribute("style") === null) {
26901             return true;
26902         }
26903         var inject = [];
26904         for (var k in this.tags) {
26905             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26906                 inject.push(k);
26907                 node.style.removeProperty(this.tags[k][0]);
26908             }
26909         }
26910         if (!inject.length) {
26911             return true; 
26912         }
26913         var cn = Array.from(node.childNodes);
26914         var nn = node;
26915         Roo.each(inject, function(t) {
26916             var nc = node.ownerDocument.createElement(t);
26917             nn.appendChild(nc);
26918             nn = nc;
26919         });
26920         for(var i = 0;i < cn.length;cn++) {
26921             node.removeChild(cn[i]);
26922             nn.appendChild(cn[i]);
26923         }
26924         return true /// iterate thru
26925     }
26926     
26927 })/**
26928  * @class Roo.htmleditor.FilterLongBr
26929  * BR/BR/BR - keep a maximum of 2...
26930  * @constructor
26931  * Run a new Long BR Filter
26932  * @param {Object} config Configuration options
26933  */
26934
26935 Roo.htmleditor.FilterLongBr = function(cfg)
26936 {
26937     // no need to apply config.
26938     this.walk(cfg.node);
26939 }
26940
26941 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26942 {
26943     
26944      
26945     tag : 'BR',
26946     
26947      
26948     replaceTag : function(node)
26949     {
26950         
26951         var ps = node.nextSibling;
26952         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26953             ps = ps.nextSibling;
26954         }
26955         
26956         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
26957             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26958             return false;
26959         }
26960         
26961         if (!ps || ps.nodeType != 1) {
26962             return false;
26963         }
26964         
26965         if (!ps || ps.tagName != 'BR') {
26966            
26967             return false;
26968         }
26969         
26970         
26971         
26972         
26973         
26974         if (!node.previousSibling) {
26975             return false;
26976         }
26977         var ps = node.previousSibling;
26978         
26979         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26980             ps = ps.previousSibling;
26981         }
26982         if (!ps || ps.nodeType != 1) {
26983             return false;
26984         }
26985         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26986         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26987             return false;
26988         }
26989         
26990         node.parentNode.removeChild(node); // remove me...
26991         
26992         return false; // no need to do children
26993
26994     }
26995     
26996 }); 
26997
26998 /**
26999  * @class Roo.htmleditor.FilterBlock
27000  * removes id / data-block and contenteditable that are associated with blocks
27001  * usage should be done on a cloned copy of the dom
27002  * @constructor
27003 * Run a new Attribute Filter { node : xxxx }}
27004 * @param {Object} config Configuration options
27005  */
27006 Roo.htmleditor.FilterBlock = function(cfg)
27007 {
27008     Roo.apply(this, cfg);
27009     var qa = cfg.node.querySelectorAll;
27010     this.removeAttributes('data-block');
27011     this.removeAttributes('contenteditable');
27012     this.removeAttributes('id');
27013     
27014 }
27015
27016 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27017 {
27018     node: true, // all tags
27019      
27020      
27021     removeAttributes : function(attr)
27022     {
27023         var ar = this.node.querySelectorAll('*[' + attr + ']');
27024         for (var i =0;i<ar.length;i++) {
27025             ar[i].removeAttribute(attr);
27026         }
27027     }
27028         
27029         
27030         
27031     
27032 });
27033 /**
27034  * @class Roo.htmleditor.KeyEnter
27035  * Handle Enter press..
27036  * @cfg {Roo.HtmlEditorCore} core the editor.
27037  * @constructor
27038  * Create a new Filter.
27039  * @param {Object} config Configuration options
27040  */
27041
27042
27043
27044
27045
27046 Roo.htmleditor.KeyEnter = function(cfg) {
27047     Roo.apply(this, cfg);
27048     // this does not actually call walk as it's really just a abstract class
27049  
27050     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
27051 }
27052
27053 //Roo.htmleditor.KeyEnter.i = 0;
27054
27055
27056 Roo.htmleditor.KeyEnter.prototype = {
27057     
27058     core : false,
27059     
27060     keypress : function(e)
27061     {
27062         if (e.charCode != 13 && e.charCode != 10) {
27063             Roo.log([e.charCode,e]);
27064             return true;
27065         }
27066         e.preventDefault();
27067         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
27068         var doc = this.core.doc;
27069           //add a new line
27070        
27071     
27072         var sel = this.core.getSelection();
27073         var range = sel.getRangeAt(0);
27074         var n = range.commonAncestorContainer;
27075         var pc = range.closest([ 'ol', 'ul']);
27076         var pli = range.closest('li');
27077         if (!pc || e.ctrlKey) {
27078             // on it list, or ctrl pressed.
27079             if (!e.ctrlKey) {
27080                 sel.insertNode('br', 'after'); 
27081             } else {
27082                 // only do this if we have ctrl key..
27083                 var br = doc.createElement('br');
27084                 br.className = 'clear';
27085                 br.setAttribute('style', 'clear: both');
27086                 sel.insertNode(br, 'after'); 
27087             }
27088             
27089          
27090             this.core.undoManager.addEvent();
27091             this.core.fireEditorEvent(e);
27092             return false;
27093         }
27094         
27095         // deal with <li> insetion
27096         if (pli.innerText.trim() == '' &&
27097             pli.previousSibling &&
27098             pli.previousSibling.nodeName == 'LI' &&
27099             pli.previousSibling.innerText.trim() ==  '') {
27100             pli.parentNode.removeChild(pli.previousSibling);
27101             sel.cursorAfter(pc);
27102             this.core.undoManager.addEvent();
27103             this.core.fireEditorEvent(e);
27104             return false;
27105         }
27106     
27107         var li = doc.createElement('LI');
27108         li.innerHTML = '&nbsp;';
27109         if (!pli || !pli.firstSibling) {
27110             pc.appendChild(li);
27111         } else {
27112             pli.parentNode.insertBefore(li, pli.firstSibling);
27113         }
27114         sel.cursorText (li.firstChild);
27115       
27116         this.core.undoManager.addEvent();
27117         this.core.fireEditorEvent(e);
27118
27119         return false;
27120         
27121     
27122         
27123         
27124          
27125     }
27126 };
27127      
27128 /**
27129  * @class Roo.htmleditor.Block
27130  * Base class for html editor blocks - do not use it directly .. extend it..
27131  * @cfg {DomElement} node The node to apply stuff to.
27132  * @cfg {String} friendly_name the name that appears in the context bar about this block
27133  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
27134  
27135  * @constructor
27136  * Create a new Filter.
27137  * @param {Object} config Configuration options
27138  */
27139
27140 Roo.htmleditor.Block  = function(cfg)
27141 {
27142     // do nothing .. should not be called really.
27143 }
27144 /**
27145  * factory method to get the block from an element (using cache if necessary)
27146  * @static
27147  * @param {HtmlElement} the dom element
27148  */
27149 Roo.htmleditor.Block.factory = function(node)
27150 {
27151     var cc = Roo.htmleditor.Block.cache;
27152     var id = Roo.get(node).id;
27153     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
27154         Roo.htmleditor.Block.cache[id].readElement(node);
27155         return Roo.htmleditor.Block.cache[id];
27156     }
27157     var db  = node.getAttribute('data-block');
27158     if (!db) {
27159         db = node.nodeName.toLowerCase().toUpperCaseFirst();
27160     }
27161     var cls = Roo.htmleditor['Block' + db];
27162     if (typeof(cls) == 'undefined') {
27163         //Roo.log(node.getAttribute('data-block'));
27164         Roo.log("OOps missing block : " + 'Block' + db);
27165         return false;
27166     }
27167     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27168     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
27169 };
27170
27171 /**
27172  * initalize all Elements from content that are 'blockable'
27173  * @static
27174  * @param the body element
27175  */
27176 Roo.htmleditor.Block.initAll = function(body, type)
27177 {
27178     if (typeof(type) == 'undefined') {
27179         var ia = Roo.htmleditor.Block.initAll;
27180         ia(body,'table');
27181         ia(body,'td');
27182         ia(body,'figure');
27183         return;
27184     }
27185     Roo.each(Roo.get(body).query(type), function(e) {
27186         Roo.htmleditor.Block.factory(e);    
27187     },this);
27188 };
27189 // question goes here... do we need to clear out this cache sometimes?
27190 // or show we make it relivant to the htmleditor.
27191 Roo.htmleditor.Block.cache = {};
27192
27193 Roo.htmleditor.Block.prototype = {
27194     
27195     node : false,
27196     
27197      // used by context menu
27198     friendly_name : 'Based Block',
27199     
27200     // text for button to delete this element
27201     deleteTitle : false,
27202     
27203     context : false,
27204     /**
27205      * Update a node with values from this object
27206      * @param {DomElement} node
27207      */
27208     updateElement : function(node)
27209     {
27210         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27211     },
27212      /**
27213      * convert to plain HTML for calling insertAtCursor..
27214      */
27215     toHTML : function()
27216     {
27217         return Roo.DomHelper.markup(this.toObject());
27218     },
27219     /**
27220      * used by readEleemnt to extract data from a node
27221      * may need improving as it's pretty basic
27222      
27223      * @param {DomElement} node
27224      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27225      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27226      * @param {String} style the style property - eg. text-align
27227      */
27228     getVal : function(node, tag, attr, style)
27229     {
27230         var n = node;
27231         if (tag !== true && n.tagName != tag.toUpperCase()) {
27232             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27233             // but kiss for now.
27234             n = node.getElementsByTagName(tag).item(0);
27235         }
27236         if (!n) {
27237             return '';
27238         }
27239         if (attr === false) {
27240             return n;
27241         }
27242         if (attr == 'html') {
27243             return n.innerHTML;
27244         }
27245         if (attr == 'style') {
27246             return n.style[style]; 
27247         }
27248         
27249         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27250             
27251     },
27252     /**
27253      * create a DomHelper friendly object - for use with 
27254      * Roo.DomHelper.markup / overwrite / etc..
27255      * (override this)
27256      */
27257     toObject : function()
27258     {
27259         return {};
27260     },
27261       /**
27262      * Read a node that has a 'data-block' property - and extract the values from it.
27263      * @param {DomElement} node - the node
27264      */
27265     readElement : function(node)
27266     {
27267         
27268     } 
27269     
27270     
27271 };
27272
27273  
27274
27275 /**
27276  * @class Roo.htmleditor.BlockFigure
27277  * Block that has an image and a figcaption
27278  * @cfg {String} image_src the url for the image
27279  * @cfg {String} align (left|right) alignment for the block default left
27280  * @cfg {String} caption the text to appear below  (and in the alt tag)
27281  * @cfg {String} caption_display (block|none) display or not the caption
27282  * @cfg {String|number} image_width the width of the image number or %?
27283  * @cfg {String|number} image_height the height of the image number or %?
27284  * 
27285  * @constructor
27286  * Create a new Filter.
27287  * @param {Object} config Configuration options
27288  */
27289
27290 Roo.htmleditor.BlockFigure = function(cfg)
27291 {
27292     if (cfg.node) {
27293         this.readElement(cfg.node);
27294         this.updateElement(cfg.node);
27295     }
27296     Roo.apply(this, cfg);
27297 }
27298 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27299  
27300     
27301     // setable values.
27302     image_src: '',
27303     align: 'center',
27304     caption : '',
27305     caption_display : 'block',
27306     width : '100%',
27307     cls : '',
27308     href: '',
27309     video_url : '',
27310     
27311     // margin: '2%', not used
27312     
27313     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
27314
27315     
27316     // used by context menu
27317     friendly_name : 'Image with caption',
27318     deleteTitle : "Delete Image and Caption",
27319     
27320     contextMenu : function(toolbar)
27321     {
27322         
27323         var block = function() {
27324             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27325         };
27326         
27327         
27328         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27329         
27330         var syncValue = toolbar.editorcore.syncValue;
27331         
27332         var fields = {};
27333         
27334         return [
27335              {
27336                 xtype : 'TextItem',
27337                 text : "Source: ",
27338                 xns : rooui.Toolbar  //Boostrap?
27339             },
27340             {
27341                 xtype : 'Button',
27342                 text: 'Change Image URL',
27343                  
27344                 listeners : {
27345                     click: function (btn, state)
27346                     {
27347                         var b = block();
27348                         
27349                         Roo.MessageBox.show({
27350                             title : "Image Source URL",
27351                             msg : "Enter the url for the image",
27352                             buttons: Roo.MessageBox.OKCANCEL,
27353                             fn: function(btn, val){
27354                                 if (btn != 'ok') {
27355                                     return;
27356                                 }
27357                                 b.image_src = val;
27358                                 b.updateElement();
27359                                 syncValue();
27360                                 toolbar.editorcore.onEditorEvent();
27361                             },
27362                             minWidth:250,
27363                             prompt:true,
27364                             //multiline: multiline,
27365                             modal : true,
27366                             value : b.image_src
27367                         });
27368                     }
27369                 },
27370                 xns : rooui.Toolbar
27371             },
27372          
27373             {
27374                 xtype : 'Button',
27375                 text: 'Change Link URL',
27376                  
27377                 listeners : {
27378                     click: function (btn, state)
27379                     {
27380                         var b = block();
27381                         
27382                         Roo.MessageBox.show({
27383                             title : "Link URL",
27384                             msg : "Enter the url for the link - leave blank to have no link",
27385                             buttons: Roo.MessageBox.OKCANCEL,
27386                             fn: function(btn, val){
27387                                 if (btn != 'ok') {
27388                                     return;
27389                                 }
27390                                 b.href = val;
27391                                 b.updateElement();
27392                                 syncValue();
27393                                 toolbar.editorcore.onEditorEvent();
27394                             },
27395                             minWidth:250,
27396                             prompt:true,
27397                             //multiline: multiline,
27398                             modal : true,
27399                             value : b.href
27400                         });
27401                     }
27402                 },
27403                 xns : rooui.Toolbar
27404             },
27405             {
27406                 xtype : 'Button',
27407                 text: 'Show Video URL',
27408                  
27409                 listeners : {
27410                     click: function (btn, state)
27411                     {
27412                         Roo.MessageBox.alert("Video URL",
27413                             block().video_url == '' ? 'This image is not linked ot a video' :
27414                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27415                     }
27416                 },
27417                 xns : rooui.Toolbar
27418             },
27419             
27420             
27421             {
27422                 xtype : 'TextItem',
27423                 text : "Width: ",
27424                 xns : rooui.Toolbar  //Boostrap?
27425             },
27426             {
27427                 xtype : 'ComboBox',
27428                 allowBlank : false,
27429                 displayField : 'val',
27430                 editable : true,
27431                 listWidth : 100,
27432                 triggerAction : 'all',
27433                 typeAhead : true,
27434                 valueField : 'val',
27435                 width : 70,
27436                 name : 'width',
27437                 listeners : {
27438                     select : function (combo, r, index)
27439                     {
27440                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27441                         var b = block();
27442                         b.width = r.get('val');
27443                         b.updateElement();
27444                         syncValue();
27445                         toolbar.editorcore.onEditorEvent();
27446                     }
27447                 },
27448                 xns : rooui.form,
27449                 store : {
27450                     xtype : 'SimpleStore',
27451                     data : [
27452                         ['100%'],
27453                         ['80%'],
27454                         ['50%'],
27455                         ['20%'],
27456                         ['10%']
27457                     ],
27458                     fields : [ 'val'],
27459                     xns : Roo.data
27460                 }
27461             },
27462             {
27463                 xtype : 'TextItem',
27464                 text : "Align: ",
27465                 xns : rooui.Toolbar  //Boostrap?
27466             },
27467             {
27468                 xtype : 'ComboBox',
27469                 allowBlank : false,
27470                 displayField : 'val',
27471                 editable : true,
27472                 listWidth : 100,
27473                 triggerAction : 'all',
27474                 typeAhead : true,
27475                 valueField : 'val',
27476                 width : 70,
27477                 name : 'align',
27478                 listeners : {
27479                     select : function (combo, r, index)
27480                     {
27481                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27482                         var b = block();
27483                         b.align = r.get('val');
27484                         b.updateElement();
27485                         syncValue();
27486                         toolbar.editorcore.onEditorEvent();
27487                     }
27488                 },
27489                 xns : rooui.form,
27490                 store : {
27491                     xtype : 'SimpleStore',
27492                     data : [
27493                         ['left'],
27494                         ['right'],
27495                         ['center']
27496                     ],
27497                     fields : [ 'val'],
27498                     xns : Roo.data
27499                 }
27500             },
27501             
27502             
27503             {
27504                 xtype : 'Button',
27505                 text: 'Hide Caption',
27506                 name : 'caption_display',
27507                 pressed : false,
27508                 enableToggle : true,
27509                 setValue : function(v) {
27510                     // this trigger toggle.
27511                      
27512                     this.setText(v ? "Hide Caption" : "Show Caption");
27513                     this.setPressed(v != 'block');
27514                 },
27515                 listeners : {
27516                     toggle: function (btn, state)
27517                     {
27518                         var b  = block();
27519                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27520                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27521                         b.updateElement();
27522                         syncValue();
27523                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27524                         toolbar.editorcore.onEditorEvent();
27525                     }
27526                 },
27527                 xns : rooui.Toolbar
27528             }
27529         ];
27530         
27531     },
27532     /**
27533      * create a DomHelper friendly object - for use with
27534      * Roo.DomHelper.markup / overwrite / etc..
27535      */
27536     toObject : function()
27537     {
27538         var d = document.createElement('div');
27539         d.innerHTML = this.caption;
27540         
27541         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
27542         
27543         var iw = this.align == 'center' ? this.width : '100%';
27544         var img =   {
27545             tag : 'img',
27546             contenteditable : 'false',
27547             src : this.image_src,
27548             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27549             style: {
27550                 width : iw,
27551                 maxWidth : iw + ' !important', // this is not getting rendered?
27552                 margin : m  
27553                 
27554             }
27555         };
27556         /*
27557         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27558                     '<a href="{2}">' + 
27559                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
27560                     '</a>' + 
27561                 '</div>',
27562         */
27563                 
27564         if (this.href.length > 0) {
27565             img = {
27566                 tag : 'a',
27567                 href: this.href,
27568                 contenteditable : 'true',
27569                 cn : [
27570                     img
27571                 ]
27572             };
27573         }
27574         
27575         
27576         if (this.video_url.length > 0) {
27577             img = {
27578                 tag : 'div',
27579                 cls : this.cls,
27580                 frameborder : 0,
27581                 allowfullscreen : true,
27582                 width : 420,  // these are for video tricks - that we replace the outer
27583                 height : 315,
27584                 src : this.video_url,
27585                 cn : [
27586                     img
27587                 ]
27588             };
27589         }
27590         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27591         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27592         
27593   
27594         var ret =   {
27595             tag: 'figure',
27596             'data-block' : 'Figure',
27597             'data-width' : this.width, 
27598             contenteditable : 'false',
27599             
27600             style : {
27601                 display: 'block',
27602                 float :  this.align ,
27603                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27604                 width : this.align == 'center' ? '100%' : this.width,
27605                 margin:  '0px',
27606                 padding: this.align == 'center' ? '0' : '0 10px' ,
27607                 textAlign : this.align   // seems to work for email..
27608                 
27609             },
27610            
27611             
27612             align : this.align,
27613             cn : [
27614                 img,
27615               
27616                 {
27617                     tag: 'figcaption',
27618                     'data-display' : this.caption_display,
27619                     style : {
27620                         textAlign : 'left',
27621                         fontSize : '16px',
27622                         lineHeight : '24px',
27623                         display : this.caption_display,
27624                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
27625                         margin: m,
27626                         width: this.align == 'center' ?  this.width : '100%' 
27627                     
27628                          
27629                     },
27630                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
27631                     cn : [
27632                         {
27633                             tag: 'div',
27634                             style  : {
27635                                 marginTop : '16px',
27636                                 textAlign : 'left'
27637                             },
27638                             align: 'left',
27639                             cn : [
27640                                 {
27641                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
27642                                     tag : 'i',
27643                                     contenteditable : true,
27644                                     html : captionhtml
27645                                 }
27646                                 
27647                             ]
27648                         }
27649                         
27650                     ]
27651                     
27652                 }
27653             ]
27654         };
27655         return ret;
27656          
27657     },
27658     
27659     readElement : function(node)
27660     {
27661         // this should not really come from the link...
27662         this.video_url = this.getVal(node, 'div', 'src');
27663         this.cls = this.getVal(node, 'div', 'class');
27664         this.href = this.getVal(node, 'a', 'href');
27665         
27666         
27667         this.image_src = this.getVal(node, 'img', 'src');
27668          
27669         this.align = this.getVal(node, 'figure', 'align');
27670         var figcaption = this.getVal(node, 'figcaption', false);
27671         if (figcaption !== '') {
27672             this.caption = this.getVal(figcaption, 'i', 'html');
27673         }
27674         
27675
27676         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27677         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27678         this.width = this.getVal(node, true, 'data-width');
27679         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27680         
27681     },
27682     removeNode : function()
27683     {
27684         return this.node;
27685     }
27686     
27687   
27688    
27689      
27690     
27691     
27692     
27693     
27694 })
27695
27696  
27697
27698 /**
27699  * @class Roo.htmleditor.BlockTable
27700  * Block that manages a table
27701  * 
27702  * @constructor
27703  * Create a new Filter.
27704  * @param {Object} config Configuration options
27705  */
27706
27707 Roo.htmleditor.BlockTable = function(cfg)
27708 {
27709     if (cfg.node) {
27710         this.readElement(cfg.node);
27711         this.updateElement(cfg.node);
27712     }
27713     Roo.apply(this, cfg);
27714     if (!cfg.node) {
27715         this.rows = [];
27716         for(var r = 0; r < this.no_row; r++) {
27717             this.rows[r] = [];
27718             for(var c = 0; c < this.no_col; c++) {
27719                 this.rows[r][c] = this.emptyCell();
27720             }
27721         }
27722     }
27723     
27724     
27725 }
27726 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27727  
27728     rows : false,
27729     no_col : 1,
27730     no_row : 1,
27731     
27732     
27733     width: '100%',
27734     
27735     // used by context menu
27736     friendly_name : 'Table',
27737     deleteTitle : 'Delete Table',
27738     // context menu is drawn once..
27739     
27740     contextMenu : function(toolbar)
27741     {
27742         
27743         var block = function() {
27744             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27745         };
27746         
27747         
27748         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27749         
27750         var syncValue = toolbar.editorcore.syncValue;
27751         
27752         var fields = {};
27753         
27754         return [
27755             {
27756                 xtype : 'TextItem',
27757                 text : "Width: ",
27758                 xns : rooui.Toolbar  //Boostrap?
27759             },
27760             {
27761                 xtype : 'ComboBox',
27762                 allowBlank : false,
27763                 displayField : 'val',
27764                 editable : true,
27765                 listWidth : 100,
27766                 triggerAction : 'all',
27767                 typeAhead : true,
27768                 valueField : 'val',
27769                 width : 100,
27770                 name : 'width',
27771                 listeners : {
27772                     select : function (combo, r, index)
27773                     {
27774                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27775                         var b = block();
27776                         b.width = r.get('val');
27777                         b.updateElement();
27778                         syncValue();
27779                         toolbar.editorcore.onEditorEvent();
27780                     }
27781                 },
27782                 xns : rooui.form,
27783                 store : {
27784                     xtype : 'SimpleStore',
27785                     data : [
27786                         ['100%'],
27787                         ['auto']
27788                     ],
27789                     fields : [ 'val'],
27790                     xns : Roo.data
27791                 }
27792             },
27793             // -------- Cols
27794             
27795             {
27796                 xtype : 'TextItem',
27797                 text : "Columns: ",
27798                 xns : rooui.Toolbar  //Boostrap?
27799             },
27800          
27801             {
27802                 xtype : 'Button',
27803                 text: '-',
27804                 listeners : {
27805                     click : function (_self, e)
27806                     {
27807                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27808                         block().removeColumn();
27809                         syncValue();
27810                         toolbar.editorcore.onEditorEvent();
27811                     }
27812                 },
27813                 xns : rooui.Toolbar
27814             },
27815             {
27816                 xtype : 'Button',
27817                 text: '+',
27818                 listeners : {
27819                     click : function (_self, e)
27820                     {
27821                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27822                         block().addColumn();
27823                         syncValue();
27824                         toolbar.editorcore.onEditorEvent();
27825                     }
27826                 },
27827                 xns : rooui.Toolbar
27828             },
27829             // -------- ROWS
27830             {
27831                 xtype : 'TextItem',
27832                 text : "Rows: ",
27833                 xns : rooui.Toolbar  //Boostrap?
27834             },
27835          
27836             {
27837                 xtype : 'Button',
27838                 text: '-',
27839                 listeners : {
27840                     click : function (_self, e)
27841                     {
27842                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27843                         block().removeRow();
27844                         syncValue();
27845                         toolbar.editorcore.onEditorEvent();
27846                     }
27847                 },
27848                 xns : rooui.Toolbar
27849             },
27850             {
27851                 xtype : 'Button',
27852                 text: '+',
27853                 listeners : {
27854                     click : function (_self, e)
27855                     {
27856                         block().addRow();
27857                         syncValue();
27858                         toolbar.editorcore.onEditorEvent();
27859                     }
27860                 },
27861                 xns : rooui.Toolbar
27862             },
27863             // -------- ROWS
27864             {
27865                 xtype : 'Button',
27866                 text: 'Reset Column Widths',
27867                 listeners : {
27868                     
27869                     click : function (_self, e)
27870                     {
27871                         block().resetWidths();
27872                         syncValue();
27873                         toolbar.editorcore.onEditorEvent();
27874                     }
27875                 },
27876                 xns : rooui.Toolbar
27877             } 
27878             
27879             
27880             
27881         ];
27882         
27883     },
27884     
27885     
27886   /**
27887      * create a DomHelper friendly object - for use with
27888      * Roo.DomHelper.markup / overwrite / etc..
27889      * ?? should it be called with option to hide all editing features?
27890      */
27891     toObject : function()
27892     {
27893         
27894         var ret = {
27895             tag : 'table',
27896             contenteditable : 'false', // this stops cell selection from picking the table.
27897             'data-block' : 'Table',
27898             style : {
27899                 width:  this.width,
27900                 border : 'solid 1px #000', // ??? hard coded?
27901                 'border-collapse' : 'collapse' 
27902             },
27903             cn : [
27904                 { tag : 'tbody' , cn : [] }
27905             ]
27906         };
27907         
27908         // do we have a head = not really 
27909         var ncols = 0;
27910         Roo.each(this.rows, function( row ) {
27911             var tr = {
27912                 tag: 'tr',
27913                 style : {
27914                     margin: '6px',
27915                     border : 'solid 1px #000',
27916                     textAlign : 'left' 
27917                 },
27918                 cn : [ ]
27919             };
27920             
27921             ret.cn[0].cn.push(tr);
27922             // does the row have any properties? ?? height?
27923             var nc = 0;
27924             Roo.each(row, function( cell ) {
27925                 
27926                 var td = {
27927                     tag : 'td',
27928                     contenteditable :  'true',
27929                     'data-block' : 'Td',
27930                     html : cell.html,
27931                     style : cell.style
27932                 };
27933                 if (cell.colspan > 1) {
27934                     td.colspan = cell.colspan ;
27935                     nc += cell.colspan;
27936                 } else {
27937                     nc++;
27938                 }
27939                 if (cell.rowspan > 1) {
27940                     td.rowspan = cell.rowspan ;
27941                 }
27942                 
27943                 
27944                 // widths ?
27945                 tr.cn.push(td);
27946                     
27947                 
27948             }, this);
27949             ncols = Math.max(nc, ncols);
27950             
27951             
27952         }, this);
27953         // add the header row..
27954         
27955         ncols++;
27956          
27957         
27958         return ret;
27959          
27960     },
27961     
27962     readElement : function(node)
27963     {
27964         node  = node ? node : this.node ;
27965         this.width = this.getVal(node, true, 'style', 'width') || '100%';
27966         
27967         this.rows = [];
27968         this.no_row = 0;
27969         var trs = Array.from(node.rows);
27970         trs.forEach(function(tr) {
27971             var row =  [];
27972             this.rows.push(row);
27973             
27974             this.no_row++;
27975             var no_column = 0;
27976             Array.from(tr.cells).forEach(function(td) {
27977                 
27978                 var add = {
27979                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27980                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27981                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27982                     html : td.innerHTML
27983                 };
27984                 no_column += add.colspan;
27985                      
27986                 
27987                 row.push(add);
27988                 
27989                 
27990             },this);
27991             this.no_col = Math.max(this.no_col, no_column);
27992             
27993             
27994         },this);
27995         
27996         
27997     },
27998     normalizeRows: function()
27999     {
28000         var ret= [];
28001         var rid = -1;
28002         this.rows.forEach(function(row) {
28003             rid++;
28004             ret[rid] = [];
28005             row = this.normalizeRow(row);
28006             var cid = 0;
28007             row.forEach(function(c) {
28008                 while (typeof(ret[rid][cid]) != 'undefined') {
28009                     cid++;
28010                 }
28011                 if (typeof(ret[rid]) == 'undefined') {
28012                     ret[rid] = [];
28013                 }
28014                 ret[rid][cid] = c;
28015                 c.row = rid;
28016                 c.col = cid;
28017                 if (c.rowspan < 2) {
28018                     return;
28019                 }
28020                 
28021                 for(var i = 1 ;i < c.rowspan; i++) {
28022                     if (typeof(ret[rid+i]) == 'undefined') {
28023                         ret[rid+i] = [];
28024                     }
28025                     ret[rid+i][cid] = c;
28026                 }
28027             });
28028         }, this);
28029         return ret;
28030     
28031     },
28032     
28033     normalizeRow: function(row)
28034     {
28035         var ret= [];
28036         row.forEach(function(c) {
28037             if (c.colspan < 2) {
28038                 ret.push(c);
28039                 return;
28040             }
28041             for(var i =0 ;i < c.colspan; i++) {
28042                 ret.push(c);
28043             }
28044         });
28045         return ret;
28046     
28047     },
28048     
28049     deleteColumn : function(sel)
28050     {
28051         if (!sel || sel.type != 'col') {
28052             return;
28053         }
28054         if (this.no_col < 2) {
28055             return;
28056         }
28057         
28058         this.rows.forEach(function(row) {
28059             var cols = this.normalizeRow(row);
28060             var col = cols[sel.col];
28061             if (col.colspan > 1) {
28062                 col.colspan --;
28063             } else {
28064                 row.remove(col);
28065             }
28066             
28067         }, this);
28068         this.no_col--;
28069         
28070     },
28071     removeColumn : function()
28072     {
28073         this.deleteColumn({
28074             type: 'col',
28075             col : this.no_col-1
28076         });
28077         this.updateElement();
28078     },
28079     
28080      
28081     addColumn : function()
28082     {
28083         
28084         this.rows.forEach(function(row) {
28085             row.push(this.emptyCell());
28086            
28087         }, this);
28088         this.updateElement();
28089     },
28090     
28091     deleteRow : function(sel)
28092     {
28093         if (!sel || sel.type != 'row') {
28094             return;
28095         }
28096         
28097         if (this.no_row < 2) {
28098             return;
28099         }
28100         
28101         var rows = this.normalizeRows();
28102         
28103         
28104         rows[sel.row].forEach(function(col) {
28105             if (col.rowspan > 1) {
28106                 col.rowspan--;
28107             } else {
28108                 col.remove = 1; // flage it as removed.
28109             }
28110             
28111         }, this);
28112         var newrows = [];
28113         this.rows.forEach(function(row) {
28114             newrow = [];
28115             row.forEach(function(c) {
28116                 if (typeof(c.remove) == 'undefined') {
28117                     newrow.push(c);
28118                 }
28119                 
28120             });
28121             if (newrow.length > 0) {
28122                 newrows.push(row);
28123             }
28124         });
28125         this.rows =  newrows;
28126         
28127         
28128         
28129         this.no_row--;
28130         this.updateElement();
28131         
28132     },
28133     removeRow : function()
28134     {
28135         this.deleteRow({
28136             type: 'row',
28137             row : this.no_row-1
28138         });
28139         
28140     },
28141     
28142      
28143     addRow : function()
28144     {
28145         
28146         var row = [];
28147         for (var i = 0; i < this.no_col; i++ ) {
28148             
28149             row.push(this.emptyCell());
28150            
28151         }
28152         this.rows.push(row);
28153         this.updateElement();
28154         
28155     },
28156      
28157     // the default cell object... at present...
28158     emptyCell : function() {
28159         return (new Roo.htmleditor.BlockTd({})).toObject();
28160         
28161      
28162     },
28163     
28164     removeNode : function()
28165     {
28166         return this.node;
28167     },
28168     
28169     
28170     
28171     resetWidths : function()
28172     {
28173         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28174             var nn = Roo.htmleditor.Block.factory(n);
28175             nn.width = '';
28176             nn.updateElement(n);
28177         });
28178     }
28179     
28180     
28181     
28182     
28183 })
28184
28185 /**
28186  *
28187  * editing a TD?
28188  *
28189  * since selections really work on the table cell, then editing really should work from there
28190  *
28191  * The original plan was to support merging etc... - but that may not be needed yet..
28192  *
28193  * So this simple version will support:
28194  *   add/remove cols
28195  *   adjust the width +/-
28196  *   reset the width...
28197  *   
28198  *
28199  */
28200
28201
28202  
28203
28204 /**
28205  * @class Roo.htmleditor.BlockTable
28206  * Block that manages a table
28207  * 
28208  * @constructor
28209  * Create a new Filter.
28210  * @param {Object} config Configuration options
28211  */
28212
28213 Roo.htmleditor.BlockTd = function(cfg)
28214 {
28215     if (cfg.node) {
28216         this.readElement(cfg.node);
28217         this.updateElement(cfg.node);
28218     }
28219     Roo.apply(this, cfg);
28220      
28221     
28222     
28223 }
28224 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28225  
28226     node : false,
28227     
28228     width: '',
28229     textAlign : 'left',
28230     valign : 'top',
28231     
28232     colspan : 1,
28233     rowspan : 1,
28234     
28235     
28236     // used by context menu
28237     friendly_name : 'Table Cell',
28238     deleteTitle : false, // use our customer delete
28239     
28240     // context menu is drawn once..
28241     
28242     contextMenu : function(toolbar)
28243     {
28244         
28245         var cell = function() {
28246             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28247         };
28248         
28249         var table = function() {
28250             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28251         };
28252         
28253         var lr = false;
28254         var saveSel = function()
28255         {
28256             lr = toolbar.editorcore.getSelection().getRangeAt(0);
28257         }
28258         var restoreSel = function()
28259         {
28260             if (lr) {
28261                 (function() {
28262                     toolbar.editorcore.focus();
28263                     var cr = toolbar.editorcore.getSelection();
28264                     cr.removeAllRanges();
28265                     cr.addRange(lr);
28266                     toolbar.editorcore.onEditorEvent();
28267                 }).defer(10, this);
28268                 
28269                 
28270             }
28271         }
28272         
28273         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28274         
28275         var syncValue = toolbar.editorcore.syncValue;
28276         
28277         var fields = {};
28278         
28279         return [
28280             {
28281                 xtype : 'Button',
28282                 text : 'Edit Table',
28283                 listeners : {
28284                     click : function() {
28285                         var t = toolbar.tb.selectedNode.closest('table');
28286                         toolbar.editorcore.selectNode(t);
28287                         toolbar.editorcore.onEditorEvent();                        
28288                     }
28289                 }
28290                 
28291             },
28292               
28293            
28294              
28295             {
28296                 xtype : 'TextItem',
28297                 text : "Column Width: ",
28298                  xns : rooui.Toolbar 
28299                
28300             },
28301             {
28302                 xtype : 'Button',
28303                 text: '-',
28304                 listeners : {
28305                     click : function (_self, e)
28306                     {
28307                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28308                         cell().shrinkColumn();
28309                         syncValue();
28310                          toolbar.editorcore.onEditorEvent();
28311                     }
28312                 },
28313                 xns : rooui.Toolbar
28314             },
28315             {
28316                 xtype : 'Button',
28317                 text: '+',
28318                 listeners : {
28319                     click : function (_self, e)
28320                     {
28321                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28322                         cell().growColumn();
28323                         syncValue();
28324                         toolbar.editorcore.onEditorEvent();
28325                     }
28326                 },
28327                 xns : rooui.Toolbar
28328             },
28329             
28330             {
28331                 xtype : 'TextItem',
28332                 text : "Vertical Align: ",
28333                 xns : rooui.Toolbar  //Boostrap?
28334             },
28335             {
28336                 xtype : 'ComboBox',
28337                 allowBlank : false,
28338                 displayField : 'val',
28339                 editable : true,
28340                 listWidth : 100,
28341                 triggerAction : 'all',
28342                 typeAhead : true,
28343                 valueField : 'val',
28344                 width : 100,
28345                 name : 'valign',
28346                 listeners : {
28347                     select : function (combo, r, index)
28348                     {
28349                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28350                         var b = cell();
28351                         b.valign = r.get('val');
28352                         b.updateElement();
28353                         syncValue();
28354                         toolbar.editorcore.onEditorEvent();
28355                     }
28356                 },
28357                 xns : rooui.form,
28358                 store : {
28359                     xtype : 'SimpleStore',
28360                     data : [
28361                         ['top'],
28362                         ['middle'],
28363                         ['bottom'] // there are afew more... 
28364                     ],
28365                     fields : [ 'val'],
28366                     xns : Roo.data
28367                 }
28368             },
28369             
28370             {
28371                 xtype : 'TextItem',
28372                 text : "Merge Cells: ",
28373                  xns : rooui.Toolbar 
28374                
28375             },
28376             
28377             
28378             {
28379                 xtype : 'Button',
28380                 text: 'Right',
28381                 listeners : {
28382                     click : function (_self, e)
28383                     {
28384                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28385                         cell().mergeRight();
28386                         //block().growColumn();
28387                         syncValue();
28388                         toolbar.editorcore.onEditorEvent();
28389                     }
28390                 },
28391                 xns : rooui.Toolbar
28392             },
28393              
28394             {
28395                 xtype : 'Button',
28396                 text: 'Below',
28397                 listeners : {
28398                     click : function (_self, e)
28399                     {
28400                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28401                         cell().mergeBelow();
28402                         //block().growColumn();
28403                         syncValue();
28404                         toolbar.editorcore.onEditorEvent();
28405                     }
28406                 },
28407                 xns : rooui.Toolbar
28408             },
28409             {
28410                 xtype : 'TextItem',
28411                 text : "| ",
28412                  xns : rooui.Toolbar 
28413                
28414             },
28415             
28416             {
28417                 xtype : 'Button',
28418                 text: 'Split',
28419                 listeners : {
28420                     click : function (_self, e)
28421                     {
28422                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28423                         cell().split();
28424                         syncValue();
28425                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28426                         toolbar.editorcore.onEditorEvent();
28427                                              
28428                     }
28429                 },
28430                 xns : rooui.Toolbar
28431             },
28432             {
28433                 xtype : 'Fill',
28434                 xns : rooui.Toolbar 
28435                
28436             },
28437         
28438           
28439             {
28440                 xtype : 'Button',
28441                 text: 'Delete',
28442                  
28443                 xns : rooui.Toolbar,
28444                 menu : {
28445                     xtype : 'Menu',
28446                     xns : rooui.menu,
28447                     items : [
28448                         {
28449                             xtype : 'Item',
28450                             html: 'Column',
28451                             listeners : {
28452                                 click : function (_self, e)
28453                                 {
28454                                     var t = table();
28455                                     
28456                                     cell().deleteColumn();
28457                                     syncValue();
28458                                     toolbar.editorcore.selectNode(t.node);
28459                                     toolbar.editorcore.onEditorEvent();   
28460                                 }
28461                             },
28462                             xns : rooui.menu
28463                         },
28464                         {
28465                             xtype : 'Item',
28466                             html: 'Row',
28467                             listeners : {
28468                                 click : function (_self, e)
28469                                 {
28470                                     var t = table();
28471                                     cell().deleteRow();
28472                                     syncValue();
28473                                     
28474                                     toolbar.editorcore.selectNode(t.node);
28475                                     toolbar.editorcore.onEditorEvent();   
28476                                                          
28477                                 }
28478                             },
28479                             xns : rooui.menu
28480                         },
28481                        {
28482                             xtype : 'Separator',
28483                             xns : rooui.menu
28484                         },
28485                         {
28486                             xtype : 'Item',
28487                             html: 'Table',
28488                             listeners : {
28489                                 click : function (_self, e)
28490                                 {
28491                                     var t = table();
28492                                     var nn = t.node.nextSibling || t.node.previousSibling;
28493                                     t.node.parentNode.removeChild(t.node);
28494                                     if (nn) { 
28495                                         toolbar.editorcore.selectNode(nn, true);
28496                                     }
28497                                     toolbar.editorcore.onEditorEvent();   
28498                                                          
28499                                 }
28500                             },
28501                             xns : rooui.menu
28502                         }
28503                     ]
28504                 }
28505             }
28506             
28507             // align... << fixme
28508             
28509         ];
28510         
28511     },
28512     
28513     
28514   /**
28515      * create a DomHelper friendly object - for use with
28516      * Roo.DomHelper.markup / overwrite / etc..
28517      * ?? should it be called with option to hide all editing features?
28518      */
28519  /**
28520      * create a DomHelper friendly object - for use with
28521      * Roo.DomHelper.markup / overwrite / etc..
28522      * ?? should it be called with option to hide all editing features?
28523      */
28524     toObject : function()
28525     {
28526         var ret = {
28527             tag : 'td',
28528             contenteditable : 'true', // this stops cell selection from picking the table.
28529             'data-block' : 'Td',
28530             valign : this.valign,
28531             style : {  
28532                 'text-align' :  this.textAlign,
28533                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28534                 'border-collapse' : 'collapse',
28535                 padding : '6px', // 8 for desktop / 4 for mobile
28536                 'vertical-align': this.valign
28537             },
28538             html : this.html
28539         };
28540         if (this.width != '') {
28541             ret.width = this.width;
28542             ret.style.width = this.width;
28543         }
28544         
28545         
28546         if (this.colspan > 1) {
28547             ret.colspan = this.colspan ;
28548         } 
28549         if (this.rowspan > 1) {
28550             ret.rowspan = this.rowspan ;
28551         }
28552         
28553            
28554         
28555         return ret;
28556          
28557     },
28558     
28559     readElement : function(node)
28560     {
28561         node  = node ? node : this.node ;
28562         this.width = node.style.width;
28563         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28564         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28565         this.html = node.innerHTML;
28566         if (node.style.textAlign != '') {
28567             this.textAlign = node.style.textAlign;
28568         }
28569         
28570         
28571     },
28572      
28573     // the default cell object... at present...
28574     emptyCell : function() {
28575         return {
28576             colspan :  1,
28577             rowspan :  1,
28578             textAlign : 'left',
28579             html : "&nbsp;" // is this going to be editable now?
28580         };
28581      
28582     },
28583     
28584     removeNode : function()
28585     {
28586         return this.node.closest('table');
28587          
28588     },
28589     
28590     cellData : false,
28591     
28592     colWidths : false,
28593     
28594     toTableArray  : function()
28595     {
28596         var ret = [];
28597         var tab = this.node.closest('tr').closest('table');
28598         Array.from(tab.rows).forEach(function(r, ri){
28599             ret[ri] = [];
28600         });
28601         var rn = 0;
28602         this.colWidths = [];
28603         var all_auto = true;
28604         Array.from(tab.rows).forEach(function(r, ri){
28605             
28606             var cn = 0;
28607             Array.from(r.cells).forEach(function(ce, ci){
28608                 var c =  {
28609                     cell : ce,
28610                     row : rn,
28611                     col: cn,
28612                     colspan : ce.colSpan,
28613                     rowspan : ce.rowSpan
28614                 };
28615                 if (ce.isEqualNode(this.node)) {
28616                     this.cellData = c;
28617                 }
28618                 // if we have been filled up by a row?
28619                 if (typeof(ret[rn][cn]) != 'undefined') {
28620                     while(typeof(ret[rn][cn]) != 'undefined') {
28621                         cn++;
28622                     }
28623                     c.col = cn;
28624                 }
28625                 
28626                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
28627                     this.colWidths[cn] =   ce.style.width;
28628                     if (this.colWidths[cn] != '') {
28629                         all_auto = false;
28630                     }
28631                 }
28632                 
28633                 
28634                 if (c.colspan < 2 && c.rowspan < 2 ) {
28635                     ret[rn][cn] = c;
28636                     cn++;
28637                     return;
28638                 }
28639                 for(var j = 0; j < c.rowspan; j++) {
28640                     if (typeof(ret[rn+j]) == 'undefined') {
28641                         continue; // we have a problem..
28642                     }
28643                     ret[rn+j][cn] = c;
28644                     for(var i = 0; i < c.colspan; i++) {
28645                         ret[rn+j][cn+i] = c;
28646                     }
28647                 }
28648                 
28649                 cn += c.colspan;
28650             }, this);
28651             rn++;
28652         }, this);
28653         
28654         // initalize widths.?
28655         // either all widths or no widths..
28656         if (all_auto) {
28657             this.colWidths[0] = false; // no widths flag.
28658         }
28659         
28660         
28661         return ret;
28662         
28663     },
28664     
28665     
28666     
28667     
28668     mergeRight: function()
28669     {
28670          
28671         // get the contents of the next cell along..
28672         var tr = this.node.closest('tr');
28673         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28674         if (i >= tr.childNodes.length - 1) {
28675             return; // no cells on right to merge with.
28676         }
28677         var table = this.toTableArray();
28678         
28679         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28680             return; // nothing right?
28681         }
28682         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28683         // right cell - must be same rowspan and on the same row.
28684         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28685             return; // right hand side is not same rowspan.
28686         }
28687         
28688         
28689         
28690         this.node.innerHTML += ' ' + rc.cell.innerHTML;
28691         tr.removeChild(rc.cell);
28692         this.colspan += rc.colspan;
28693         this.node.setAttribute('colspan', this.colspan);
28694
28695         var table = this.toTableArray();
28696         this.normalizeWidths(table);
28697         this.updateWidths(table);
28698     },
28699     
28700     
28701     mergeBelow : function()
28702     {
28703         var table = this.toTableArray();
28704         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28705             return; // no row below
28706         }
28707         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28708             return; // nothing right?
28709         }
28710         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28711         
28712         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28713             return; // right hand side is not same rowspan.
28714         }
28715         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
28716         rc.cell.parentNode.removeChild(rc.cell);
28717         this.rowspan += rc.rowspan;
28718         this.node.setAttribute('rowspan', this.rowspan);
28719     },
28720     
28721     split: function()
28722     {
28723         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28724             return;
28725         }
28726         var table = this.toTableArray();
28727         var cd = this.cellData;
28728         this.rowspan = 1;
28729         this.colspan = 1;
28730         
28731         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28732              
28733             
28734             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28735                 if (r == cd.row && c == cd.col) {
28736                     this.node.removeAttribute('rowspan');
28737                     this.node.removeAttribute('colspan');
28738                 }
28739                  
28740                 var ntd = this.node.cloneNode(); // which col/row should be 0..
28741                 ntd.removeAttribute('id'); 
28742                 ntd.style.width  = this.colWidths[c];
28743                 ntd.innerHTML = '';
28744                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
28745             }
28746             
28747         }
28748         this.redrawAllCells(table);
28749         
28750     },
28751     
28752     
28753     
28754     redrawAllCells: function(table)
28755     {
28756         
28757          
28758         var tab = this.node.closest('tr').closest('table');
28759         var ctr = tab.rows[0].parentNode;
28760         Array.from(tab.rows).forEach(function(r, ri){
28761             
28762             Array.from(r.cells).forEach(function(ce, ci){
28763                 ce.parentNode.removeChild(ce);
28764             });
28765             r.parentNode.removeChild(r);
28766         });
28767         for(var r = 0 ; r < table.length; r++) {
28768             var re = tab.rows[r];
28769             
28770             var re = tab.ownerDocument.createElement('tr');
28771             ctr.appendChild(re);
28772             for(var c = 0 ; c < table[r].length; c++) {
28773                 if (table[r][c].cell === false) {
28774                     continue;
28775                 }
28776                 
28777                 re.appendChild(table[r][c].cell);
28778                  
28779                 table[r][c].cell = false;
28780             }
28781         }
28782         
28783     },
28784     updateWidths : function(table)
28785     {
28786         for(var r = 0 ; r < table.length; r++) {
28787            
28788             for(var c = 0 ; c < table[r].length; c++) {
28789                 if (table[r][c].cell === false) {
28790                     continue;
28791                 }
28792                 
28793                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28794                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28795                     el.width = Math.floor(this.colWidths[c])  +'%';
28796                     el.updateElement(el.node);
28797                 }
28798                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
28799                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28800                     var width = 0;
28801                     for(var i = 0; i < table[r][c].colspan; i ++) {
28802                         width += Math.floor(this.colWidths[c + i]);
28803                     }
28804                     el.width = width  +'%';
28805                     el.updateElement(el.node);
28806                 }
28807                 table[r][c].cell = false; // done
28808             }
28809         }
28810     },
28811     normalizeWidths : function(table)
28812     {
28813         if (this.colWidths[0] === false) {
28814             var nw = 100.0 / this.colWidths.length;
28815             this.colWidths.forEach(function(w,i) {
28816                 this.colWidths[i] = nw;
28817             },this);
28818             return;
28819         }
28820     
28821         var t = 0, missing = [];
28822         
28823         this.colWidths.forEach(function(w,i) {
28824             //if you mix % and
28825             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28826             var add =  this.colWidths[i];
28827             if (add > 0) {
28828                 t+=add;
28829                 return;
28830             }
28831             missing.push(i);
28832             
28833             
28834         },this);
28835         var nc = this.colWidths.length;
28836         if (missing.length) {
28837             var mult = (nc - missing.length) / (1.0 * nc);
28838             var t = mult * t;
28839             var ew = (100 -t) / (1.0 * missing.length);
28840             this.colWidths.forEach(function(w,i) {
28841                 if (w > 0) {
28842                     this.colWidths[i] = w * mult;
28843                     return;
28844                 }
28845                 
28846                 this.colWidths[i] = ew;
28847             }, this);
28848             // have to make up numbers..
28849              
28850         }
28851         // now we should have all the widths..
28852         
28853     
28854     },
28855     
28856     shrinkColumn : function()
28857     {
28858         var table = this.toTableArray();
28859         this.normalizeWidths(table);
28860         var col = this.cellData.col;
28861         var nw = this.colWidths[col] * 0.8;
28862         if (nw < 5) {
28863             return;
28864         }
28865         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28866         this.colWidths.forEach(function(w,i) {
28867             if (i == col) {
28868                  this.colWidths[i] = nw;
28869                 return;
28870             }
28871             this.colWidths[i] += otherAdd
28872         }, this);
28873         this.updateWidths(table);
28874          
28875     },
28876     growColumn : function()
28877     {
28878         var table = this.toTableArray();
28879         this.normalizeWidths(table);
28880         var col = this.cellData.col;
28881         var nw = this.colWidths[col] * 1.2;
28882         if (nw > 90) {
28883             return;
28884         }
28885         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
28886         this.colWidths.forEach(function(w,i) {
28887             if (i == col) {
28888                 this.colWidths[i] = nw;
28889                 return;
28890             }
28891             this.colWidths[i] -= otherSub
28892         }, this);
28893         this.updateWidths(table);
28894          
28895     },
28896     deleteRow : function()
28897     {
28898         // delete this rows 'tr'
28899         // if any of the cells in this row have a rowspan > 1 && row!= this row..
28900         // then reduce the rowspan.
28901         var table = this.toTableArray();
28902         // this.cellData.row;
28903         for (var i =0;i< table[this.cellData.row].length ; i++) {
28904             var c = table[this.cellData.row][i];
28905             if (c.row != this.cellData.row) {
28906                 
28907                 c.rowspan--;
28908                 c.cell.setAttribute('rowspan', c.rowspan);
28909                 continue;
28910             }
28911             if (c.rowspan > 1) {
28912                 c.rowspan--;
28913                 c.cell.setAttribute('rowspan', c.rowspan);
28914             }
28915         }
28916         table.splice(this.cellData.row,1);
28917         this.redrawAllCells(table);
28918         
28919     },
28920     deleteColumn : function()
28921     {
28922         var table = this.toTableArray();
28923         
28924         for (var i =0;i< table.length ; i++) {
28925             var c = table[i][this.cellData.col];
28926             if (c.col != this.cellData.col) {
28927                 table[i][this.cellData.col].colspan--;
28928             } else if (c.colspan > 1) {
28929                 c.colspan--;
28930                 c.cell.setAttribute('colspan', c.colspan);
28931             }
28932             table[i].splice(this.cellData.col,1);
28933         }
28934         
28935         this.redrawAllCells(table);
28936     }
28937     
28938     
28939     
28940     
28941 })
28942
28943 //<script type="text/javascript">
28944
28945 /*
28946  * Based  Ext JS Library 1.1.1
28947  * Copyright(c) 2006-2007, Ext JS, LLC.
28948  * LGPL
28949  *
28950  */
28951  
28952 /**
28953  * @class Roo.HtmlEditorCore
28954  * @extends Roo.Component
28955  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28956  *
28957  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28958  */
28959
28960 Roo.HtmlEditorCore = function(config){
28961     
28962     
28963     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28964     
28965     
28966     this.addEvents({
28967         /**
28968          * @event initialize
28969          * Fires when the editor is fully initialized (including the iframe)
28970          * @param {Roo.HtmlEditorCore} this
28971          */
28972         initialize: true,
28973         /**
28974          * @event activate
28975          * Fires when the editor is first receives the focus. Any insertion must wait
28976          * until after this event.
28977          * @param {Roo.HtmlEditorCore} this
28978          */
28979         activate: true,
28980          /**
28981          * @event beforesync
28982          * Fires before the textarea is updated with content from the editor iframe. Return false
28983          * to cancel the sync.
28984          * @param {Roo.HtmlEditorCore} this
28985          * @param {String} html
28986          */
28987         beforesync: true,
28988          /**
28989          * @event beforepush
28990          * Fires before the iframe editor is updated with content from the textarea. Return false
28991          * to cancel the push.
28992          * @param {Roo.HtmlEditorCore} this
28993          * @param {String} html
28994          */
28995         beforepush: true,
28996          /**
28997          * @event sync
28998          * Fires when the textarea is updated with content from the editor iframe.
28999          * @param {Roo.HtmlEditorCore} this
29000          * @param {String} html
29001          */
29002         sync: true,
29003          /**
29004          * @event push
29005          * Fires when the iframe editor is updated with content from the textarea.
29006          * @param {Roo.HtmlEditorCore} this
29007          * @param {String} html
29008          */
29009         push: true,
29010         
29011         /**
29012          * @event editorevent
29013          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
29014          * @param {Roo.HtmlEditorCore} this
29015          */
29016         editorevent: true 
29017          
29018         
29019     });
29020     
29021     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
29022     
29023     // defaults : white / black...
29024     this.applyBlacklists();
29025     
29026     
29027     
29028 };
29029
29030
29031 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
29032
29033
29034      /**
29035      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
29036      */
29037     
29038     owner : false,
29039     
29040      /**
29041      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
29042      *                        Roo.resizable.
29043      */
29044     resizable : false,
29045      /**
29046      * @cfg {Number} height (in pixels)
29047      */   
29048     height: 300,
29049    /**
29050      * @cfg {Number} width (in pixels)
29051      */   
29052     width: 500,
29053      /**
29054      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
29055      *         if you are doing an email editor, this probably needs disabling, it's designed
29056      */
29057     autoClean: true,
29058     
29059     /**
29060      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
29061      */
29062     enableBlocks : true,
29063     /**
29064      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
29065      * 
29066      */
29067     stylesheets: false,
29068      /**
29069      * @cfg {String} language default en - language of text (usefull for rtl languages)
29070      * 
29071      */
29072     language: 'en',
29073     
29074     /**
29075      * @cfg {boolean} allowComments - default false - allow comments in HTML source
29076      *          - by default they are stripped - if you are editing email you may need this.
29077      */
29078     allowComments: false,
29079     // id of frame..
29080     frameId: false,
29081     
29082     // private properties
29083     validationEvent : false,
29084     deferHeight: true,
29085     initialized : false,
29086     activated : false,
29087     sourceEditMode : false,
29088     onFocus : Roo.emptyFn,
29089     iframePad:3,
29090     hideMode:'offsets',
29091     
29092     clearUp: true,
29093     
29094     // blacklist + whitelisted elements..
29095     black: false,
29096     white: false,
29097      
29098     bodyCls : '',
29099
29100     
29101     undoManager : false,
29102     /**
29103      * Protected method that will not generally be called directly. It
29104      * is called when the editor initializes the iframe with HTML contents. Override this method if you
29105      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
29106      */
29107     getDocMarkup : function(){
29108         // body styles..
29109         var st = '';
29110         
29111         // inherit styels from page...?? 
29112         if (this.stylesheets === false) {
29113             
29114             Roo.get(document.head).select('style').each(function(node) {
29115                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29116             });
29117             
29118             Roo.get(document.head).select('link').each(function(node) { 
29119                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
29120             });
29121             
29122         } else if (!this.stylesheets.length) {
29123                 // simple..
29124                 st = '<style type="text/css">' +
29125                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29126                    '</style>';
29127         } else {
29128             for (var i in this.stylesheets) {
29129                 if (typeof(this.stylesheets[i]) != 'string') {
29130                     continue;
29131                 }
29132                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
29133             }
29134             
29135         }
29136         
29137         st +=  '<style type="text/css">' +
29138             'IMG { cursor: pointer } ' +
29139         '</style>';
29140         
29141         st += '<meta name="google" content="notranslate">';
29142         
29143         var cls = 'notranslate roo-htmleditor-body';
29144         
29145         if(this.bodyCls.length){
29146             cls += ' ' + this.bodyCls;
29147         }
29148         
29149         return '<html  class="notranslate" translate="no"><head>' + st  +
29150             //<style type="text/css">' +
29151             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
29152             //'</style>' +
29153             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
29154     },
29155
29156     // private
29157     onRender : function(ct, position)
29158     {
29159         var _t = this;
29160         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
29161         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
29162         
29163         
29164         this.el.dom.style.border = '0 none';
29165         this.el.dom.setAttribute('tabIndex', -1);
29166         this.el.addClass('x-hidden hide');
29167         
29168         
29169         
29170         if(Roo.isIE){ // fix IE 1px bogus margin
29171             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29172         }
29173        
29174         
29175         this.frameId = Roo.id();
29176         
29177          
29178         
29179         var iframe = this.owner.wrap.createChild({
29180             tag: 'iframe',
29181             cls: 'form-control', // bootstrap..
29182             id: this.frameId,
29183             name: this.frameId,
29184             frameBorder : 'no',
29185             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
29186         }, this.el
29187         );
29188         
29189         
29190         this.iframe = iframe.dom;
29191
29192         this.assignDocWin();
29193         
29194         this.doc.designMode = 'on';
29195        
29196         this.doc.open();
29197         this.doc.write(this.getDocMarkup());
29198         this.doc.close();
29199
29200         
29201         var task = { // must defer to wait for browser to be ready
29202             run : function(){
29203                 //console.log("run task?" + this.doc.readyState);
29204                 this.assignDocWin();
29205                 if(this.doc.body || this.doc.readyState == 'complete'){
29206                     try {
29207                         this.doc.designMode="on";
29208                         
29209                     } catch (e) {
29210                         return;
29211                     }
29212                     Roo.TaskMgr.stop(task);
29213                     this.initEditor.defer(10, this);
29214                 }
29215             },
29216             interval : 10,
29217             duration: 10000,
29218             scope: this
29219         };
29220         Roo.TaskMgr.start(task);
29221
29222     },
29223
29224     // private
29225     onResize : function(w, h)
29226     {
29227          Roo.log('resize: ' +w + ',' + h );
29228         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29229         if(!this.iframe){
29230             return;
29231         }
29232         if(typeof w == 'number'){
29233             
29234             this.iframe.style.width = w + 'px';
29235         }
29236         if(typeof h == 'number'){
29237             
29238             this.iframe.style.height = h + 'px';
29239             if(this.doc){
29240                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29241             }
29242         }
29243         
29244     },
29245
29246     /**
29247      * Toggles the editor between standard and source edit mode.
29248      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29249      */
29250     toggleSourceEdit : function(sourceEditMode){
29251         
29252         this.sourceEditMode = sourceEditMode === true;
29253         
29254         if(this.sourceEditMode){
29255  
29256             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
29257             
29258         }else{
29259             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29260             //this.iframe.className = '';
29261             this.deferFocus();
29262         }
29263         //this.setSize(this.owner.wrap.getSize());
29264         //this.fireEvent('editmodechange', this, this.sourceEditMode);
29265     },
29266
29267     
29268   
29269
29270     /**
29271      * Protected method that will not generally be called directly. If you need/want
29272      * custom HTML cleanup, this is the method you should override.
29273      * @param {String} html The HTML to be cleaned
29274      * return {String} The cleaned HTML
29275      */
29276     cleanHtml : function(html)
29277     {
29278         html = String(html);
29279         if(html.length > 5){
29280             if(Roo.isSafari){ // strip safari nonsense
29281                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29282             }
29283         }
29284         if(html == '&nbsp;'){
29285             html = '';
29286         }
29287         return html;
29288     },
29289
29290     /**
29291      * HTML Editor -> Textarea
29292      * Protected method that will not generally be called directly. Syncs the contents
29293      * of the editor iframe with the textarea.
29294      */
29295     syncValue : function()
29296     {
29297         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29298         if(this.initialized){
29299             
29300             if (this.undoManager) {
29301                 this.undoManager.addEvent();
29302             }
29303
29304             
29305             var bd = (this.doc.body || this.doc.documentElement);
29306            
29307             
29308             var sel = this.win.getSelection();
29309             
29310             var div = document.createElement('div');
29311             div.innerHTML = bd.innerHTML;
29312             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29313             if (gtx.length > 0) {
29314                 var rm = gtx.item(0).parentNode;
29315                 rm.parentNode.removeChild(rm);
29316             }
29317             
29318            
29319             if (this.enableBlocks) {
29320                 new Roo.htmleditor.FilterBlock({ node : div });
29321             }
29322             
29323             var html = div.innerHTML;
29324             
29325             //?? tidy?
29326             if (this.autoClean) {
29327                 
29328                 new Roo.htmleditor.FilterAttributes({
29329                     node : div,
29330                     attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29331                     attrib_clean : ['href', 'src' ] 
29332                 });
29333                 
29334                 var tidy = new Roo.htmleditor.TidySerializer({
29335                     inner:  true
29336                 });
29337                 html  = tidy.serialize(div);
29338                 
29339             }
29340             
29341             
29342             if(Roo.isSafari){
29343                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29344                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29345                 if(m && m[1]){
29346                     html = '<div style="'+m[0]+'">' + html + '</div>';
29347                 }
29348             }
29349             html = this.cleanHtml(html);
29350             // fix up the special chars.. normaly like back quotes in word...
29351             // however we do not want to do this with chinese..
29352             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29353                 
29354                 var cc = match.charCodeAt();
29355
29356                 // Get the character value, handling surrogate pairs
29357                 if (match.length == 2) {
29358                     // It's a surrogate pair, calculate the Unicode code point
29359                     var high = match.charCodeAt(0) - 0xD800;
29360                     var low  = match.charCodeAt(1) - 0xDC00;
29361                     cc = (high * 0x400) + low + 0x10000;
29362                 }  else if (
29363                     (cc >= 0x4E00 && cc < 0xA000 ) ||
29364                     (cc >= 0x3400 && cc < 0x4E00 ) ||
29365                     (cc >= 0xf900 && cc < 0xfb00 )
29366                 ) {
29367                         return match;
29368                 }  
29369          
29370                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29371                 return "&#" + cc + ";";
29372                 
29373                 
29374             });
29375             
29376             
29377              
29378             if(this.owner.fireEvent('beforesync', this, html) !== false){
29379                 this.el.dom.value = html;
29380                 this.owner.fireEvent('sync', this, html);
29381             }
29382         }
29383     },
29384
29385     /**
29386      * TEXTAREA -> EDITABLE
29387      * Protected method that will not generally be called directly. Pushes the value of the textarea
29388      * into the iframe editor.
29389      */
29390     pushValue : function()
29391     {
29392         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29393         if(this.initialized){
29394             var v = this.el.dom.value.trim();
29395             
29396             
29397             if(this.owner.fireEvent('beforepush', this, v) !== false){
29398                 var d = (this.doc.body || this.doc.documentElement);
29399                 d.innerHTML = v;
29400                  
29401                 this.el.dom.value = d.innerHTML;
29402                 this.owner.fireEvent('push', this, v);
29403             }
29404             if (this.autoClean) {
29405                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29406                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29407             }
29408             if (this.enableBlocks) {
29409                 Roo.htmleditor.Block.initAll(this.doc.body);
29410             }
29411             
29412             this.updateLanguage();
29413             
29414             var lc = this.doc.body.lastChild;
29415             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29416                 // add an extra line at the end.
29417                 this.doc.body.appendChild(this.doc.createElement('br'));
29418             }
29419             
29420             
29421         }
29422     },
29423
29424     // private
29425     deferFocus : function(){
29426         this.focus.defer(10, this);
29427     },
29428
29429     // doc'ed in Field
29430     focus : function(){
29431         if(this.win && !this.sourceEditMode){
29432             this.win.focus();
29433         }else{
29434             this.el.focus();
29435         }
29436     },
29437     
29438     assignDocWin: function()
29439     {
29440         var iframe = this.iframe;
29441         
29442          if(Roo.isIE){
29443             this.doc = iframe.contentWindow.document;
29444             this.win = iframe.contentWindow;
29445         } else {
29446 //            if (!Roo.get(this.frameId)) {
29447 //                return;
29448 //            }
29449 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29450 //            this.win = Roo.get(this.frameId).dom.contentWindow;
29451             
29452             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29453                 return;
29454             }
29455             
29456             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29457             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29458         }
29459     },
29460     
29461     // private
29462     initEditor : function(){
29463         //console.log("INIT EDITOR");
29464         this.assignDocWin();
29465         
29466         
29467         
29468         this.doc.designMode="on";
29469         this.doc.open();
29470         this.doc.write(this.getDocMarkup());
29471         this.doc.close();
29472         
29473         var dbody = (this.doc.body || this.doc.documentElement);
29474         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29475         // this copies styles from the containing element into thsi one..
29476         // not sure why we need all of this..
29477         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29478         
29479         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29480         //ss['background-attachment'] = 'fixed'; // w3c
29481         dbody.bgProperties = 'fixed'; // ie
29482         dbody.setAttribute("translate", "no");
29483         
29484         //Roo.DomHelper.applyStyles(dbody, ss);
29485         Roo.EventManager.on(this.doc, {
29486              
29487             'mouseup': this.onEditorEvent,
29488             'dblclick': this.onEditorEvent,
29489             'click': this.onEditorEvent,
29490             'keyup': this.onEditorEvent,
29491             
29492             buffer:100,
29493             scope: this
29494         });
29495         Roo.EventManager.on(this.doc, {
29496             'paste': this.onPasteEvent,
29497             scope : this
29498         });
29499         if(Roo.isGecko){
29500             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29501         }
29502         //??? needed???
29503         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29504             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29505         }
29506         this.initialized = true;
29507
29508         
29509         // initialize special key events - enter
29510         new Roo.htmleditor.KeyEnter({core : this});
29511         
29512          
29513         
29514         this.owner.fireEvent('initialize', this);
29515         this.pushValue();
29516     },
29517     // this is to prevent a href clicks resulting in a redirect?
29518    
29519     onPasteEvent : function(e,v)
29520     {
29521         // I think we better assume paste is going to be a dirty load of rubish from word..
29522         
29523         // even pasting into a 'email version' of this widget will have to clean up that mess.
29524         var cd = (e.browserEvent.clipboardData || window.clipboardData);
29525         
29526         // check what type of paste - if it's an image, then handle it differently.
29527         if (cd.files && cd.files.length > 0) {
29528             // pasting images?
29529             var urlAPI = (window.createObjectURL && window) || 
29530                 (window.URL && URL.revokeObjectURL && URL) || 
29531                 (window.webkitURL && webkitURL);
29532     
29533             var url = urlAPI.createObjectURL( cd.files[0]);
29534             this.insertAtCursor('<img src=" + url + ">');
29535             return false;
29536         }
29537         if (cd.types.indexOf('text/html') < 0 ) {
29538             return false;
29539         }
29540         var images = [];
29541         var html = cd.getData('text/html'); // clipboard event
29542         if (cd.types.indexOf('text/rtf') > -1) {
29543             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29544             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29545         }
29546         //Roo.log(images);
29547         //Roo.log(imgs);
29548         // fixme..
29549         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29550                        .map(function(g) { return g.toDataURL(); })
29551                        .filter(function(g) { return g != 'about:blank'; });
29552         
29553         //Roo.log(html);
29554         html = this.cleanWordChars(html);
29555         
29556         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29557         
29558         
29559         var sn = this.getParentElement();
29560         // check if d contains a table, and prevent nesting??
29561         //Roo.log(d.getElementsByTagName('table'));
29562         //Roo.log(sn);
29563         //Roo.log(sn.closest('table'));
29564         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29565             e.preventDefault();
29566             this.insertAtCursor("You can not nest tables");
29567             //Roo.log("prevent?"); // fixme - 
29568             return false;
29569         }
29570         
29571         
29572         
29573         if (images.length > 0) {
29574             // replace all v:imagedata - with img.
29575             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
29576             Roo.each(ar, function(node) {
29577                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
29578                 node.parentNode.removeChild(node);
29579             });
29580             
29581             
29582             Roo.each(d.getElementsByTagName('img'), function(img, i) {
29583                 img.setAttribute('src', images[i]);
29584             });
29585         }
29586         if (this.autoClean) {
29587             new Roo.htmleditor.FilterWord({ node : d });
29588             
29589             new Roo.htmleditor.FilterStyleToTag({ node : d });
29590             new Roo.htmleditor.FilterAttributes({
29591                 node : d,
29592                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
29593                 attrib_clean : ['href', 'src' ] 
29594             });
29595             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29596             // should be fonts..
29597             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
29598             new Roo.htmleditor.FilterParagraph({ node : d });
29599             new Roo.htmleditor.FilterSpan({ node : d });
29600             new Roo.htmleditor.FilterLongBr({ node : d });
29601             new Roo.htmleditor.FilterComment({ node : d });
29602             
29603             
29604         }
29605         if (this.enableBlocks) {
29606                 
29607             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29608                 if (img.closest('figure')) { // assume!! that it's aready
29609                     return;
29610                 }
29611                 var fig  = new Roo.htmleditor.BlockFigure({
29612                     image_src  : img.src
29613                 });
29614                 fig.updateElement(img); // replace it..
29615                 
29616             });
29617         }
29618         
29619         
29620         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
29621         if (this.enableBlocks) {
29622             Roo.htmleditor.Block.initAll(this.doc.body);
29623         }
29624          
29625         
29626         e.preventDefault();
29627         return false;
29628         // default behaveiour should be our local cleanup paste? (optional?)
29629         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29630         //this.owner.fireEvent('paste', e, v);
29631     },
29632     // private
29633     onDestroy : function(){
29634         
29635         
29636         
29637         if(this.rendered){
29638             
29639             //for (var i =0; i < this.toolbars.length;i++) {
29640             //    // fixme - ask toolbars for heights?
29641             //    this.toolbars[i].onDestroy();
29642            // }
29643             
29644             //this.wrap.dom.innerHTML = '';
29645             //this.wrap.remove();
29646         }
29647     },
29648
29649     // private
29650     onFirstFocus : function(){
29651         
29652         this.assignDocWin();
29653         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29654         
29655         this.activated = true;
29656          
29657     
29658         if(Roo.isGecko){ // prevent silly gecko errors
29659             this.win.focus();
29660             var s = this.win.getSelection();
29661             if(!s.focusNode || s.focusNode.nodeType != 3){
29662                 var r = s.getRangeAt(0);
29663                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29664                 r.collapse(true);
29665                 this.deferFocus();
29666             }
29667             try{
29668                 this.execCmd('useCSS', true);
29669                 this.execCmd('styleWithCSS', false);
29670             }catch(e){}
29671         }
29672         this.owner.fireEvent('activate', this);
29673     },
29674
29675     // private
29676     adjustFont: function(btn){
29677         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29678         //if(Roo.isSafari){ // safari
29679         //    adjust *= 2;
29680        // }
29681         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29682         if(Roo.isSafari){ // safari
29683             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29684             v =  (v < 10) ? 10 : v;
29685             v =  (v > 48) ? 48 : v;
29686             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29687             
29688         }
29689         
29690         
29691         v = Math.max(1, v+adjust);
29692         
29693         this.execCmd('FontSize', v  );
29694     },
29695
29696     onEditorEvent : function(e)
29697     {
29698          
29699         
29700         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29701             return; // we do not handle this.. (undo manager does..)
29702         }
29703         // in theory this detects if the last element is not a br, then we try and do that.
29704         // its so clicking in space at bottom triggers adding a br and moving the cursor.
29705         if (e &&
29706             e.target.nodeName == 'BODY' &&
29707             e.type == "mouseup" &&
29708             this.doc.body.lastChild
29709            ) {
29710             var lc = this.doc.body.lastChild;
29711             // gtx-trans is google translate plugin adding crap.
29712             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29713                 lc = lc.previousSibling;
29714             }
29715             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29716             // if last element is <BR> - then dont do anything.
29717             
29718                 var ns = this.doc.createElement('br');
29719                 this.doc.body.appendChild(ns);
29720                 range = this.doc.createRange();
29721                 range.setStartAfter(ns);
29722                 range.collapse(true);
29723                 var sel = this.win.getSelection();
29724                 sel.removeAllRanges();
29725                 sel.addRange(range);
29726             }
29727         }
29728         
29729         
29730         
29731         this.fireEditorEvent(e);
29732       //  this.updateToolbar();
29733         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29734     },
29735     
29736     fireEditorEvent: function(e)
29737     {
29738         this.owner.fireEvent('editorevent', this, e);
29739     },
29740
29741     insertTag : function(tg)
29742     {
29743         // could be a bit smarter... -> wrap the current selected tRoo..
29744         if (tg.toLowerCase() == 'span' ||
29745             tg.toLowerCase() == 'code' ||
29746             tg.toLowerCase() == 'sup' ||
29747             tg.toLowerCase() == 'sub' 
29748             ) {
29749             
29750             range = this.createRange(this.getSelection());
29751             var wrappingNode = this.doc.createElement(tg.toLowerCase());
29752             wrappingNode.appendChild(range.extractContents());
29753             range.insertNode(wrappingNode);
29754
29755             return;
29756             
29757             
29758             
29759         }
29760         this.execCmd("formatblock",   tg);
29761         this.undoManager.addEvent(); 
29762     },
29763     
29764     insertText : function(txt)
29765     {
29766         
29767         
29768         var range = this.createRange();
29769         range.deleteContents();
29770                //alert(Sender.getAttribute('label'));
29771                
29772         range.insertNode(this.doc.createTextNode(txt));
29773         this.undoManager.addEvent();
29774     } ,
29775     
29776      
29777
29778     /**
29779      * Executes a Midas editor command on the editor document and performs necessary focus and
29780      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29781      * @param {String} cmd The Midas command
29782      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29783      */
29784     relayCmd : function(cmd, value)
29785     {
29786         
29787         switch (cmd) {
29788             case 'justifyleft':
29789             case 'justifyright':
29790             case 'justifycenter':
29791                 // if we are in a cell, then we will adjust the
29792                 var n = this.getParentElement();
29793                 var td = n.closest('td');
29794                 if (td) {
29795                     var bl = Roo.htmleditor.Block.factory(td);
29796                     bl.textAlign = cmd.replace('justify','');
29797                     bl.updateElement();
29798                     this.owner.fireEvent('editorevent', this);
29799                     return;
29800                 }
29801                 this.execCmd('styleWithCSS', true); // 
29802                 break;
29803             case 'bold':
29804             case 'italic':
29805                 // if there is no selection, then we insert, and set the curson inside it..
29806                 this.execCmd('styleWithCSS', false); 
29807                 break;
29808                 
29809         
29810             default:
29811                 break;
29812         }
29813         
29814         
29815         this.win.focus();
29816         this.execCmd(cmd, value);
29817         this.owner.fireEvent('editorevent', this);
29818         //this.updateToolbar();
29819         this.owner.deferFocus();
29820     },
29821
29822     /**
29823      * Executes a Midas editor command directly on the editor document.
29824      * For visual commands, you should use {@link #relayCmd} instead.
29825      * <b>This should only be called after the editor is initialized.</b>
29826      * @param {String} cmd The Midas command
29827      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29828      */
29829     execCmd : function(cmd, value){
29830         this.doc.execCommand(cmd, false, value === undefined ? null : value);
29831         this.syncValue();
29832     },
29833  
29834  
29835    
29836     /**
29837      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29838      * to insert tRoo.
29839      * @param {String} text | dom node.. 
29840      */
29841     insertAtCursor : function(text)
29842     {
29843         
29844         if(!this.activated){
29845             return;
29846         }
29847          
29848         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29849             this.win.focus();
29850             
29851             
29852             // from jquery ui (MIT licenced)
29853             var range, node;
29854             var win = this.win;
29855             
29856             if (win.getSelection && win.getSelection().getRangeAt) {
29857                 
29858                 // delete the existing?
29859                 
29860                 this.createRange(this.getSelection()).deleteContents();
29861                 range = win.getSelection().getRangeAt(0);
29862                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29863                 range.insertNode(node);
29864                 range = range.cloneRange();
29865                 range.collapse(false);
29866                  
29867                 win.getSelection().removeAllRanges();
29868                 win.getSelection().addRange(range);
29869                 
29870                 
29871                 
29872             } else if (win.document.selection && win.document.selection.createRange) {
29873                 // no firefox support
29874                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29875                 win.document.selection.createRange().pasteHTML(txt);
29876             
29877             } else {
29878                 // no firefox support
29879                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29880                 this.execCmd('InsertHTML', txt);
29881             } 
29882             this.syncValue();
29883             
29884             this.deferFocus();
29885         }
29886     },
29887  // private
29888     mozKeyPress : function(e){
29889         if(e.ctrlKey){
29890             var c = e.getCharCode(), cmd;
29891           
29892             if(c > 0){
29893                 c = String.fromCharCode(c).toLowerCase();
29894                 switch(c){
29895                     case 'b':
29896                         cmd = 'bold';
29897                         break;
29898                     case 'i':
29899                         cmd = 'italic';
29900                         break;
29901                     
29902                     case 'u':
29903                         cmd = 'underline';
29904                         break;
29905                     
29906                     //case 'v':
29907                       //  this.cleanUpPaste.defer(100, this);
29908                       //  return;
29909                         
29910                 }
29911                 if(cmd){
29912                     
29913                     this.relayCmd(cmd);
29914                     //this.win.focus();
29915                     //this.execCmd(cmd);
29916                     //this.deferFocus();
29917                     e.preventDefault();
29918                 }
29919                 
29920             }
29921         }
29922     },
29923
29924     // private
29925     fixKeys : function(){ // load time branching for fastest keydown performance
29926         
29927         
29928         if(Roo.isIE){
29929             return function(e){
29930                 var k = e.getKey(), r;
29931                 if(k == e.TAB){
29932                     e.stopEvent();
29933                     r = this.doc.selection.createRange();
29934                     if(r){
29935                         r.collapse(true);
29936                         r.pasteHTML('&#160;&#160;&#160;&#160;');
29937                         this.deferFocus();
29938                     }
29939                     return;
29940                 }
29941                 /// this is handled by Roo.htmleditor.KeyEnter
29942                  /*
29943                 if(k == e.ENTER){
29944                     r = this.doc.selection.createRange();
29945                     if(r){
29946                         var target = r.parentElement();
29947                         if(!target || target.tagName.toLowerCase() != 'li'){
29948                             e.stopEvent();
29949                             r.pasteHTML('<br/>');
29950                             r.collapse(false);
29951                             r.select();
29952                         }
29953                     }
29954                 }
29955                 */
29956                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29957                 //    this.cleanUpPaste.defer(100, this);
29958                 //    return;
29959                 //}
29960                 
29961                 
29962             };
29963         }else if(Roo.isOpera){
29964             return function(e){
29965                 var k = e.getKey();
29966                 if(k == e.TAB){
29967                     e.stopEvent();
29968                     this.win.focus();
29969                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
29970                     this.deferFocus();
29971                 }
29972                
29973                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29974                 //    this.cleanUpPaste.defer(100, this);
29975                  //   return;
29976                 //}
29977                 
29978             };
29979         }else if(Roo.isSafari){
29980             return function(e){
29981                 var k = e.getKey();
29982                 
29983                 if(k == e.TAB){
29984                     e.stopEvent();
29985                     this.execCmd('InsertText','\t');
29986                     this.deferFocus();
29987                     return;
29988                 }
29989                  this.mozKeyPress(e);
29990                 
29991                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29992                  //   this.cleanUpPaste.defer(100, this);
29993                  //   return;
29994                // }
29995                 
29996              };
29997         }
29998     }(),
29999     
30000     getAllAncestors: function()
30001     {
30002         var p = this.getSelectedNode();
30003         var a = [];
30004         if (!p) {
30005             a.push(p); // push blank onto stack..
30006             p = this.getParentElement();
30007         }
30008         
30009         
30010         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
30011             a.push(p);
30012             p = p.parentNode;
30013         }
30014         a.push(this.doc.body);
30015         return a;
30016     },
30017     lastSel : false,
30018     lastSelNode : false,
30019     
30020     
30021     getSelection : function() 
30022     {
30023         this.assignDocWin();
30024         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
30025     },
30026     /**
30027      * Select a dom node
30028      * @param {DomElement} node the node to select
30029      */
30030     selectNode : function(node, collapse)
30031     {
30032         var nodeRange = node.ownerDocument.createRange();
30033         try {
30034             nodeRange.selectNode(node);
30035         } catch (e) {
30036             nodeRange.selectNodeContents(node);
30037         }
30038         if (collapse === true) {
30039             nodeRange.collapse(true);
30040         }
30041         //
30042         var s = this.win.getSelection();
30043         s.removeAllRanges();
30044         s.addRange(nodeRange);
30045     },
30046     
30047     getSelectedNode: function() 
30048     {
30049         // this may only work on Gecko!!!
30050         
30051         // should we cache this!!!!
30052         
30053          
30054          
30055         var range = this.createRange(this.getSelection()).cloneRange();
30056         
30057         if (Roo.isIE) {
30058             var parent = range.parentElement();
30059             while (true) {
30060                 var testRange = range.duplicate();
30061                 testRange.moveToElementText(parent);
30062                 if (testRange.inRange(range)) {
30063                     break;
30064                 }
30065                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
30066                     break;
30067                 }
30068                 parent = parent.parentElement;
30069             }
30070             return parent;
30071         }
30072         
30073         // is ancestor a text element.
30074         var ac =  range.commonAncestorContainer;
30075         if (ac.nodeType == 3) {
30076             ac = ac.parentNode;
30077         }
30078         
30079         var ar = ac.childNodes;
30080          
30081         var nodes = [];
30082         var other_nodes = [];
30083         var has_other_nodes = false;
30084         for (var i=0;i<ar.length;i++) {
30085             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
30086                 continue;
30087             }
30088             // fullly contained node.
30089             
30090             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
30091                 nodes.push(ar[i]);
30092                 continue;
30093             }
30094             
30095             // probably selected..
30096             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
30097                 other_nodes.push(ar[i]);
30098                 continue;
30099             }
30100             // outer..
30101             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
30102                 continue;
30103             }
30104             
30105             
30106             has_other_nodes = true;
30107         }
30108         if (!nodes.length && other_nodes.length) {
30109             nodes= other_nodes;
30110         }
30111         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
30112             return false;
30113         }
30114         
30115         return nodes[0];
30116     },
30117     
30118     
30119     createRange: function(sel)
30120     {
30121         // this has strange effects when using with 
30122         // top toolbar - not sure if it's a great idea.
30123         //this.editor.contentWindow.focus();
30124         if (typeof sel != "undefined") {
30125             try {
30126                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
30127             } catch(e) {
30128                 return this.doc.createRange();
30129             }
30130         } else {
30131             return this.doc.createRange();
30132         }
30133     },
30134     getParentElement: function()
30135     {
30136         
30137         this.assignDocWin();
30138         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
30139         
30140         var range = this.createRange(sel);
30141          
30142         try {
30143             var p = range.commonAncestorContainer;
30144             while (p.nodeType == 3) { // text node
30145                 p = p.parentNode;
30146             }
30147             return p;
30148         } catch (e) {
30149             return null;
30150         }
30151     
30152     },
30153     /***
30154      *
30155      * Range intersection.. the hard stuff...
30156      *  '-1' = before
30157      *  '0' = hits..
30158      *  '1' = after.
30159      *         [ -- selected range --- ]
30160      *   [fail]                        [fail]
30161      *
30162      *    basically..
30163      *      if end is before start or  hits it. fail.
30164      *      if start is after end or hits it fail.
30165      *
30166      *   if either hits (but other is outside. - then it's not 
30167      *   
30168      *    
30169      **/
30170     
30171     
30172     // @see http://www.thismuchiknow.co.uk/?p=64.
30173     rangeIntersectsNode : function(range, node)
30174     {
30175         var nodeRange = node.ownerDocument.createRange();
30176         try {
30177             nodeRange.selectNode(node);
30178         } catch (e) {
30179             nodeRange.selectNodeContents(node);
30180         }
30181     
30182         var rangeStartRange = range.cloneRange();
30183         rangeStartRange.collapse(true);
30184     
30185         var rangeEndRange = range.cloneRange();
30186         rangeEndRange.collapse(false);
30187     
30188         var nodeStartRange = nodeRange.cloneRange();
30189         nodeStartRange.collapse(true);
30190     
30191         var nodeEndRange = nodeRange.cloneRange();
30192         nodeEndRange.collapse(false);
30193     
30194         return rangeStartRange.compareBoundaryPoints(
30195                  Range.START_TO_START, nodeEndRange) == -1 &&
30196                rangeEndRange.compareBoundaryPoints(
30197                  Range.START_TO_START, nodeStartRange) == 1;
30198         
30199          
30200     },
30201     rangeCompareNode : function(range, node)
30202     {
30203         var nodeRange = node.ownerDocument.createRange();
30204         try {
30205             nodeRange.selectNode(node);
30206         } catch (e) {
30207             nodeRange.selectNodeContents(node);
30208         }
30209         
30210         
30211         range.collapse(true);
30212     
30213         nodeRange.collapse(true);
30214      
30215         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30216         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
30217          
30218         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30219         
30220         var nodeIsBefore   =  ss == 1;
30221         var nodeIsAfter    = ee == -1;
30222         
30223         if (nodeIsBefore && nodeIsAfter) {
30224             return 0; // outer
30225         }
30226         if (!nodeIsBefore && nodeIsAfter) {
30227             return 1; //right trailed.
30228         }
30229         
30230         if (nodeIsBefore && !nodeIsAfter) {
30231             return 2;  // left trailed.
30232         }
30233         // fully contined.
30234         return 3;
30235     },
30236  
30237     cleanWordChars : function(input) {// change the chars to hex code
30238         
30239        var swapCodes  = [ 
30240             [    8211, "&#8211;" ], 
30241             [    8212, "&#8212;" ], 
30242             [    8216,  "'" ],  
30243             [    8217, "'" ],  
30244             [    8220, '"' ],  
30245             [    8221, '"' ],  
30246             [    8226, "*" ],  
30247             [    8230, "..." ]
30248         ]; 
30249         var output = input;
30250         Roo.each(swapCodes, function(sw) { 
30251             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30252             
30253             output = output.replace(swapper, sw[1]);
30254         });
30255         
30256         return output;
30257     },
30258     
30259      
30260     
30261         
30262     
30263     cleanUpChild : function (node)
30264     {
30265         
30266         new Roo.htmleditor.FilterComment({node : node});
30267         new Roo.htmleditor.FilterAttributes({
30268                 node : node,
30269                 attrib_black : this.ablack,
30270                 attrib_clean : this.aclean,
30271                 style_white : this.cwhite,
30272                 style_black : this.cblack
30273         });
30274         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30275         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30276          
30277         
30278     },
30279     
30280     /**
30281      * Clean up MS wordisms...
30282      * @deprecated - use filter directly
30283      */
30284     cleanWord : function(node)
30285     {
30286         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30287         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
30288         
30289     },
30290    
30291     
30292     /**
30293
30294      * @deprecated - use filters
30295      */
30296     cleanTableWidths : function(node)
30297     {
30298         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30299         
30300  
30301     },
30302     
30303      
30304         
30305     applyBlacklists : function()
30306     {
30307         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
30308         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
30309         
30310         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
30311         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
30312         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
30313         
30314         this.white = [];
30315         this.black = [];
30316         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30317             if (b.indexOf(tag) > -1) {
30318                 return;
30319             }
30320             this.white.push(tag);
30321             
30322         }, this);
30323         
30324         Roo.each(w, function(tag) {
30325             if (b.indexOf(tag) > -1) {
30326                 return;
30327             }
30328             if (this.white.indexOf(tag) > -1) {
30329                 return;
30330             }
30331             this.white.push(tag);
30332             
30333         }, this);
30334         
30335         
30336         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30337             if (w.indexOf(tag) > -1) {
30338                 return;
30339             }
30340             this.black.push(tag);
30341             
30342         }, this);
30343         
30344         Roo.each(b, function(tag) {
30345             if (w.indexOf(tag) > -1) {
30346                 return;
30347             }
30348             if (this.black.indexOf(tag) > -1) {
30349                 return;
30350             }
30351             this.black.push(tag);
30352             
30353         }, this);
30354         
30355         
30356         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
30357         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
30358         
30359         this.cwhite = [];
30360         this.cblack = [];
30361         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30362             if (b.indexOf(tag) > -1) {
30363                 return;
30364             }
30365             this.cwhite.push(tag);
30366             
30367         }, this);
30368         
30369         Roo.each(w, function(tag) {
30370             if (b.indexOf(tag) > -1) {
30371                 return;
30372             }
30373             if (this.cwhite.indexOf(tag) > -1) {
30374                 return;
30375             }
30376             this.cwhite.push(tag);
30377             
30378         }, this);
30379         
30380         
30381         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30382             if (w.indexOf(tag) > -1) {
30383                 return;
30384             }
30385             this.cblack.push(tag);
30386             
30387         }, this);
30388         
30389         Roo.each(b, function(tag) {
30390             if (w.indexOf(tag) > -1) {
30391                 return;
30392             }
30393             if (this.cblack.indexOf(tag) > -1) {
30394                 return;
30395             }
30396             this.cblack.push(tag);
30397             
30398         }, this);
30399     },
30400     
30401     setStylesheets : function(stylesheets)
30402     {
30403         if(typeof(stylesheets) == 'string'){
30404             Roo.get(this.iframe.contentDocument.head).createChild({
30405                 tag : 'link',
30406                 rel : 'stylesheet',
30407                 type : 'text/css',
30408                 href : stylesheets
30409             });
30410             
30411             return;
30412         }
30413         var _this = this;
30414      
30415         Roo.each(stylesheets, function(s) {
30416             if(!s.length){
30417                 return;
30418             }
30419             
30420             Roo.get(_this.iframe.contentDocument.head).createChild({
30421                 tag : 'link',
30422                 rel : 'stylesheet',
30423                 type : 'text/css',
30424                 href : s
30425             });
30426         });
30427
30428         
30429     },
30430     
30431     
30432     updateLanguage : function()
30433     {
30434         if (!this.iframe || !this.iframe.contentDocument) {
30435             return;
30436         }
30437         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30438     },
30439     
30440     
30441     removeStylesheets : function()
30442     {
30443         var _this = this;
30444         
30445         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30446             s.remove();
30447         });
30448     },
30449     
30450     setStyle : function(style)
30451     {
30452         Roo.get(this.iframe.contentDocument.head).createChild({
30453             tag : 'style',
30454             type : 'text/css',
30455             html : style
30456         });
30457
30458         return;
30459     }
30460     
30461     // hide stuff that is not compatible
30462     /**
30463      * @event blur
30464      * @hide
30465      */
30466     /**
30467      * @event change
30468      * @hide
30469      */
30470     /**
30471      * @event focus
30472      * @hide
30473      */
30474     /**
30475      * @event specialkey
30476      * @hide
30477      */
30478     /**
30479      * @cfg {String} fieldClass @hide
30480      */
30481     /**
30482      * @cfg {String} focusClass @hide
30483      */
30484     /**
30485      * @cfg {String} autoCreate @hide
30486      */
30487     /**
30488      * @cfg {String} inputType @hide
30489      */
30490     /**
30491      * @cfg {String} invalidClass @hide
30492      */
30493     /**
30494      * @cfg {String} invalidText @hide
30495      */
30496     /**
30497      * @cfg {String} msgFx @hide
30498      */
30499     /**
30500      * @cfg {String} validateOnBlur @hide
30501      */
30502 });
30503
30504 Roo.HtmlEditorCore.white = [
30505         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30506         
30507        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
30508        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
30509        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
30510        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
30511        'TABLE',   'UL',         'XMP', 
30512        
30513        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
30514       'THEAD',   'TR', 
30515      
30516       'DIR', 'MENU', 'OL', 'UL', 'DL',
30517        
30518       'EMBED',  'OBJECT'
30519 ];
30520
30521
30522 Roo.HtmlEditorCore.black = [
30523     //    'embed',  'object', // enable - backend responsiblity to clean thiese
30524         'APPLET', // 
30525         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
30526         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
30527         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
30528         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
30529         //'FONT' // CLEAN LATER..
30530         'COLGROUP', 'COL'   // messy tables.
30531         
30532         
30533 ];
30534 Roo.HtmlEditorCore.clean = [ // ?? needed???
30535      'SCRIPT', 'STYLE', 'TITLE', 'XML'
30536 ];
30537 Roo.HtmlEditorCore.tag_remove = [
30538     'FONT', 'TBODY'  
30539 ];
30540 // attributes..
30541
30542 Roo.HtmlEditorCore.ablack = [
30543     'on'
30544 ];
30545     
30546 Roo.HtmlEditorCore.aclean = [ 
30547     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
30548 ];
30549
30550 // protocols..
30551 Roo.HtmlEditorCore.pwhite= [
30552         'http',  'https',  'mailto'
30553 ];
30554
30555 // white listed style attributes.
30556 Roo.HtmlEditorCore.cwhite= [
30557       //  'text-align', /// default is to allow most things..
30558       
30559          
30560 //        'font-size'//??
30561 ];
30562
30563 // black listed style attributes.
30564 Roo.HtmlEditorCore.cblack= [
30565       //  'font-size' -- this can be set by the project 
30566 ];
30567
30568
30569
30570
30571     /*
30572  * - LGPL
30573  *
30574  * HtmlEditor
30575  * 
30576  */
30577
30578 /**
30579  * @class Roo.bootstrap.form.HtmlEditor
30580  * @extends Roo.bootstrap.form.TextArea
30581  * Bootstrap HtmlEditor class
30582
30583  * @constructor
30584  * Create a new HtmlEditor
30585  * @param {Object} config The config object
30586  */
30587
30588 Roo.bootstrap.form.HtmlEditor = function(config){
30589     Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30590     if (!this.toolbars) {
30591         this.toolbars = [];
30592     }
30593     
30594     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30595     this.addEvents({
30596             /**
30597              * @event initialize
30598              * Fires when the editor is fully initialized (including the iframe)
30599              * @param {HtmlEditor} this
30600              */
30601             initialize: true,
30602             /**
30603              * @event activate
30604              * Fires when the editor is first receives the focus. Any insertion must wait
30605              * until after this event.
30606              * @param {HtmlEditor} this
30607              */
30608             activate: true,
30609              /**
30610              * @event beforesync
30611              * Fires before the textarea is updated with content from the editor iframe. Return false
30612              * to cancel the sync.
30613              * @param {HtmlEditor} this
30614              * @param {String} html
30615              */
30616             beforesync: true,
30617              /**
30618              * @event beforepush
30619              * Fires before the iframe editor is updated with content from the textarea. Return false
30620              * to cancel the push.
30621              * @param {HtmlEditor} this
30622              * @param {String} html
30623              */
30624             beforepush: true,
30625              /**
30626              * @event sync
30627              * Fires when the textarea is updated with content from the editor iframe.
30628              * @param {HtmlEditor} this
30629              * @param {String} html
30630              */
30631             sync: true,
30632              /**
30633              * @event push
30634              * Fires when the iframe editor is updated with content from the textarea.
30635              * @param {HtmlEditor} this
30636              * @param {String} html
30637              */
30638             push: true,
30639              /**
30640              * @event editmodechange
30641              * Fires when the editor switches edit modes
30642              * @param {HtmlEditor} this
30643              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30644              */
30645             editmodechange: true,
30646             /**
30647              * @event editorevent
30648              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30649              * @param {HtmlEditor} this
30650              */
30651             editorevent: true,
30652             /**
30653              * @event firstfocus
30654              * Fires when on first focus - needed by toolbars..
30655              * @param {HtmlEditor} this
30656              */
30657             firstfocus: true,
30658             /**
30659              * @event autosave
30660              * Auto save the htmlEditor value as a file into Events
30661              * @param {HtmlEditor} this
30662              */
30663             autosave: true,
30664             /**
30665              * @event savedpreview
30666              * preview the saved version of htmlEditor
30667              * @param {HtmlEditor} this
30668              */
30669             savedpreview: true
30670         });
30671 };
30672
30673
30674 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea,  {
30675     
30676     
30677       /**
30678      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30679      */
30680     toolbars : false,
30681     
30682      /**
30683     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30684     */
30685     btns : [],
30686    
30687      /**
30688      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
30689      *                        Roo.resizable.
30690      */
30691     resizable : false,
30692      /**
30693      * @cfg {Number} height (in pixels)
30694      */   
30695     height: 300,
30696    /**
30697      * @cfg {Number} width (in pixels)
30698      */   
30699     width: false,
30700     
30701     /**
30702      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30703      * 
30704      */
30705     stylesheets: false,
30706     
30707     // id of frame..
30708     frameId: false,
30709     
30710     // private properties
30711     validationEvent : false,
30712     deferHeight: true,
30713     initialized : false,
30714     activated : false,
30715     
30716     onFocus : Roo.emptyFn,
30717     iframePad:3,
30718     hideMode:'offsets',
30719     
30720     tbContainer : false,
30721     
30722     bodyCls : '',
30723     
30724     toolbarContainer :function() {
30725         return this.wrap.select('.x-html-editor-tb',true).first();
30726     },
30727
30728     /**
30729      * Protected method that will not generally be called directly. It
30730      * is called when the editor creates its toolbar. Override this method if you need to
30731      * add custom toolbar buttons.
30732      * @param {HtmlEditor} editor
30733      */
30734     createToolbar : function(){
30735         Roo.log('renewing');
30736         Roo.log("create toolbars");
30737         
30738         this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30739         this.toolbars[0].render(this.toolbarContainer());
30740         
30741         return;
30742         
30743 //        if (!editor.toolbars || !editor.toolbars.length) {
30744 //            editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30745 //        }
30746 //        
30747 //        for (var i =0 ; i < editor.toolbars.length;i++) {
30748 //            editor.toolbars[i] = Roo.factory(
30749 //                    typeof(editor.toolbars[i]) == 'string' ?
30750 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
30751 //                Roo.bootstrap.form.HtmlEditor);
30752 //            editor.toolbars[i].init(editor);
30753 //        }
30754     },
30755
30756      
30757     // private
30758     onRender : function(ct, position)
30759     {
30760        // Roo.log("Call onRender: " + this.xtype);
30761         var _t = this;
30762         Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30763       
30764         this.wrap = this.inputEl().wrap({
30765             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30766         });
30767         
30768         this.editorcore.onRender(ct, position);
30769          
30770         if (this.resizable) {
30771             this.resizeEl = new Roo.Resizable(this.wrap, {
30772                 pinned : true,
30773                 wrap: true,
30774                 dynamic : true,
30775                 minHeight : this.height,
30776                 height: this.height,
30777                 handles : this.resizable,
30778                 width: this.width,
30779                 listeners : {
30780                     resize : function(r, w, h) {
30781                         _t.onResize(w,h); // -something
30782                     }
30783                 }
30784             });
30785             
30786         }
30787         this.createToolbar(this);
30788        
30789         
30790         if(!this.width && this.resizable){
30791             this.setSize(this.wrap.getSize());
30792         }
30793         if (this.resizeEl) {
30794             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30795             // should trigger onReize..
30796         }
30797         
30798     },
30799
30800     // private
30801     onResize : function(w, h)
30802     {
30803         Roo.log('resize: ' +w + ',' + h );
30804         Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30805         var ew = false;
30806         var eh = false;
30807         
30808         if(this.inputEl() ){
30809             if(typeof w == 'number'){
30810                 var aw = w - this.wrap.getFrameWidth('lr');
30811                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30812                 ew = aw;
30813             }
30814             if(typeof h == 'number'){
30815                  var tbh = -11;  // fixme it needs to tool bar size!
30816                 for (var i =0; i < this.toolbars.length;i++) {
30817                     // fixme - ask toolbars for heights?
30818                     tbh += this.toolbars[i].el.getHeight();
30819                     //if (this.toolbars[i].footer) {
30820                     //    tbh += this.toolbars[i].footer.el.getHeight();
30821                     //}
30822                 }
30823               
30824                 
30825                 
30826                 
30827                 
30828                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30829                 ah -= 5; // knock a few pixes off for look..
30830                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30831                 var eh = ah;
30832             }
30833         }
30834         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30835         this.editorcore.onResize(ew,eh);
30836         
30837     },
30838
30839     /**
30840      * Toggles the editor between standard and source edit mode.
30841      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30842      */
30843     toggleSourceEdit : function(sourceEditMode)
30844     {
30845         this.editorcore.toggleSourceEdit(sourceEditMode);
30846         
30847         if(this.editorcore.sourceEditMode){
30848             Roo.log('editor - showing textarea');
30849             
30850 //            Roo.log('in');
30851 //            Roo.log(this.syncValue());
30852             this.syncValue();
30853             this.inputEl().removeClass(['hide', 'x-hidden']);
30854             this.inputEl().dom.removeAttribute('tabIndex');
30855             this.inputEl().focus();
30856         }else{
30857             Roo.log('editor - hiding textarea');
30858 //            Roo.log('out')
30859 //            Roo.log(this.pushValue()); 
30860             this.pushValue();
30861             
30862             this.inputEl().addClass(['hide', 'x-hidden']);
30863             this.inputEl().dom.setAttribute('tabIndex', -1);
30864             //this.deferFocus();
30865         }
30866          
30867         if(this.resizable){
30868             this.setSize(this.wrap.getSize());
30869         }
30870         
30871         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30872     },
30873  
30874     // private (for BoxComponent)
30875     adjustSize : Roo.BoxComponent.prototype.adjustSize,
30876
30877     // private (for BoxComponent)
30878     getResizeEl : function(){
30879         return this.wrap;
30880     },
30881
30882     // private (for BoxComponent)
30883     getPositionEl : function(){
30884         return this.wrap;
30885     },
30886
30887     // private
30888     initEvents : function(){
30889         this.originalValue = this.getValue();
30890     },
30891
30892 //    /**
30893 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30894 //     * @method
30895 //     */
30896 //    markInvalid : Roo.emptyFn,
30897 //    /**
30898 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30899 //     * @method
30900 //     */
30901 //    clearInvalid : Roo.emptyFn,
30902
30903     setValue : function(v){
30904         Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30905         this.editorcore.pushValue();
30906     },
30907
30908      
30909     // private
30910     deferFocus : function(){
30911         this.focus.defer(10, this);
30912     },
30913
30914     // doc'ed in Field
30915     focus : function(){
30916         this.editorcore.focus();
30917         
30918     },
30919       
30920
30921     // private
30922     onDestroy : function(){
30923         
30924         
30925         
30926         if(this.rendered){
30927             
30928             for (var i =0; i < this.toolbars.length;i++) {
30929                 // fixme - ask toolbars for heights?
30930                 this.toolbars[i].onDestroy();
30931             }
30932             
30933             this.wrap.dom.innerHTML = '';
30934             this.wrap.remove();
30935         }
30936     },
30937
30938     // private
30939     onFirstFocus : function(){
30940         //Roo.log("onFirstFocus");
30941         this.editorcore.onFirstFocus();
30942          for (var i =0; i < this.toolbars.length;i++) {
30943             this.toolbars[i].onFirstFocus();
30944         }
30945         
30946     },
30947     
30948     // private
30949     syncValue : function()
30950     {   
30951         this.editorcore.syncValue();
30952     },
30953     
30954     pushValue : function()
30955     {   
30956         this.editorcore.pushValue();
30957     }
30958      
30959     
30960     // hide stuff that is not compatible
30961     /**
30962      * @event blur
30963      * @hide
30964      */
30965     /**
30966      * @event change
30967      * @hide
30968      */
30969     /**
30970      * @event focus
30971      * @hide
30972      */
30973     /**
30974      * @event specialkey
30975      * @hide
30976      */
30977     /**
30978      * @cfg {String} fieldClass @hide
30979      */
30980     /**
30981      * @cfg {String} focusClass @hide
30982      */
30983     /**
30984      * @cfg {String} autoCreate @hide
30985      */
30986     /**
30987      * @cfg {String} inputType @hide
30988      */
30989      
30990     /**
30991      * @cfg {String} invalidText @hide
30992      */
30993     /**
30994      * @cfg {String} msgFx @hide
30995      */
30996     /**
30997      * @cfg {String} validateOnBlur @hide
30998      */
30999 });
31000  
31001     
31002    
31003    
31004    
31005       
31006 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
31007 /**
31008  * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
31009  * @parent Roo.bootstrap.form.HtmlEditor
31010  * @extends Roo.bootstrap.nav.Simplebar
31011  * Basic Toolbar
31012  * 
31013  * @example
31014  * Usage:
31015  *
31016  new Roo.bootstrap.form.HtmlEditor({
31017     ....
31018     toolbars : [
31019         new Roo.bootstrap.form.HtmlEditorToolbarStandard({
31020             disable : { fonts: 1 , format: 1, ..., ... , ...],
31021             btns : [ .... ]
31022         })
31023     }
31024      
31025  * 
31026  * @cfg {Object} disable List of elements to disable..
31027  * @cfg {Array} btns List of additional buttons.
31028  * 
31029  * 
31030  * NEEDS Extra CSS? 
31031  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
31032  */
31033  
31034 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
31035 {
31036     
31037     Roo.apply(this, config);
31038     
31039     // default disabled, based on 'good practice'..
31040     this.disable = this.disable || {};
31041     Roo.applyIf(this.disable, {
31042         fontSize : true,
31043         colors : true,
31044         specialElements : true
31045     });
31046     Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
31047     
31048     this.editor = config.editor;
31049     this.editorcore = config.editor.editorcore;
31050     
31051     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
31052     
31053     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
31054     // dont call parent... till later.
31055 }
31056 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar,  {
31057      
31058     bar : true,
31059     
31060     editor : false,
31061     editorcore : false,
31062     
31063     
31064     formats : [
31065         "p" ,  
31066         "h1","h2","h3","h4","h5","h6", 
31067         "pre", "code", 
31068         "abbr", "acronym", "address", "cite", "samp", "var",
31069         'div','span'
31070     ],
31071     
31072     onRender : function(ct, position)
31073     {
31074        // Roo.log("Call onRender: " + this.xtype);
31075         
31076        Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
31077        Roo.log(this.el);
31078        this.el.dom.style.marginBottom = '0';
31079        var _this = this;
31080        var editorcore = this.editorcore;
31081        var editor= this.editor;
31082        
31083        var children = [];
31084        var btn = function(id,cmd , toggle, handler, html){
31085        
31086             var  event = toggle ? 'toggle' : 'click';
31087        
31088             var a = {
31089                 size : 'sm',
31090                 xtype: 'Button',
31091                 xns: Roo.bootstrap,
31092                 //glyphicon : id,
31093                 fa: id,
31094                 cmd : id || cmd,
31095                 enableToggle:toggle !== false,
31096                 html : html || '',
31097                 pressed : toggle ? false : null,
31098                 listeners : {}
31099             };
31100             a.listeners[toggle ? 'toggle' : 'click'] = function() {
31101                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
31102             };
31103             children.push(a);
31104             return a;
31105        }
31106        
31107     //    var cb_box = function...
31108         
31109         var style = {
31110                 xtype: 'Button',
31111                 size : 'sm',
31112                 xns: Roo.bootstrap,
31113                 fa : 'font',
31114                 //html : 'submit'
31115                 menu : {
31116                     xtype: 'Menu',
31117                     xns: Roo.bootstrap,
31118                     items:  []
31119                 }
31120         };
31121         Roo.each(this.formats, function(f) {
31122             style.menu.items.push({
31123                 xtype :'MenuItem',
31124                 xns: Roo.bootstrap,
31125                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
31126                 tagname : f,
31127                 listeners : {
31128                     click : function()
31129                     {
31130                         editorcore.insertTag(this.tagname);
31131                         editor.focus();
31132                     }
31133                 }
31134                 
31135             });
31136         });
31137         children.push(style);   
31138         
31139         btn('bold',false,true);
31140         btn('italic',false,true);
31141         btn('align-left', 'justifyleft',true);
31142         btn('align-center', 'justifycenter',true);
31143         btn('align-right' , 'justifyright',true);
31144         btn('link', false, false, function(btn) {
31145             //Roo.log("create link?");
31146             var url = prompt(this.createLinkText, this.defaultLinkValue);
31147             if(url && url != 'http:/'+'/'){
31148                 this.editorcore.relayCmd('createlink', url);
31149             }
31150         }),
31151         btn('list','insertunorderedlist',true);
31152         btn('pencil', false,true, function(btn){
31153                 Roo.log(this);
31154                 this.toggleSourceEdit(btn.pressed);
31155         });
31156         
31157         if (this.editor.btns.length > 0) {
31158             for (var i = 0; i<this.editor.btns.length; i++) {
31159                 children.push(this.editor.btns[i]);
31160             }
31161         }
31162         
31163         /*
31164         var cog = {
31165                 xtype: 'Button',
31166                 size : 'sm',
31167                 xns: Roo.bootstrap,
31168                 glyphicon : 'cog',
31169                 //html : 'submit'
31170                 menu : {
31171                     xtype: 'Menu',
31172                     xns: Roo.bootstrap,
31173                     items:  []
31174                 }
31175         };
31176         
31177         cog.menu.items.push({
31178             xtype :'MenuItem',
31179             xns: Roo.bootstrap,
31180             html : Clean styles,
31181             tagname : f,
31182             listeners : {
31183                 click : function()
31184                 {
31185                     editorcore.insertTag(this.tagname);
31186                     editor.focus();
31187                 }
31188             }
31189             
31190         });
31191        */
31192         
31193          
31194        this.xtype = 'NavSimplebar';
31195         
31196         for(var i=0;i< children.length;i++) {
31197             
31198             this.buttons.add(this.addxtypeChild(children[i]));
31199             
31200         }
31201         
31202         editor.on('editorevent', this.updateToolbar, this);
31203     },
31204     onBtnClick : function(id)
31205     {
31206        this.editorcore.relayCmd(id);
31207        this.editorcore.focus();
31208     },
31209     
31210     /**
31211      * Protected method that will not generally be called directly. It triggers
31212      * a toolbar update by reading the markup state of the current selection in the editor.
31213      */
31214     updateToolbar: function(){
31215
31216         if(!this.editorcore.activated){
31217             this.editor.onFirstFocus(); // is this neeed?
31218             return;
31219         }
31220
31221         var btns = this.buttons; 
31222         var doc = this.editorcore.doc;
31223         btns.get('bold').setActive(doc.queryCommandState('bold'));
31224         btns.get('italic').setActive(doc.queryCommandState('italic'));
31225         //btns.get('underline').setActive(doc.queryCommandState('underline'));
31226         
31227         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31228         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31229         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31230         
31231         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31232         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31233          /*
31234         
31235         var ans = this.editorcore.getAllAncestors();
31236         if (this.formatCombo) {
31237             
31238             
31239             var store = this.formatCombo.store;
31240             this.formatCombo.setValue("");
31241             for (var i =0; i < ans.length;i++) {
31242                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31243                     // select it..
31244                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31245                     break;
31246                 }
31247             }
31248         }
31249         
31250         
31251         
31252         // hides menus... - so this cant be on a menu...
31253         Roo.bootstrap.MenuMgr.hideAll();
31254         */
31255         Roo.bootstrap.menu.Manager.hideAll();
31256         //this.editorsyncValue();
31257     },
31258     onFirstFocus: function() {
31259         this.buttons.each(function(item){
31260            item.enable();
31261         });
31262     },
31263     toggleSourceEdit : function(sourceEditMode){
31264         
31265           
31266         if(sourceEditMode){
31267             Roo.log("disabling buttons");
31268            this.buttons.each( function(item){
31269                 if(item.cmd != 'pencil'){
31270                     item.disable();
31271                 }
31272             });
31273           
31274         }else{
31275             Roo.log("enabling buttons");
31276             if(this.editorcore.initialized){
31277                 this.buttons.each( function(item){
31278                     item.enable();
31279                 });
31280             }
31281             
31282         }
31283         Roo.log("calling toggole on editor");
31284         // tell the editor that it's been pressed..
31285         this.editor.toggleSourceEdit(sourceEditMode);
31286        
31287     }
31288 });
31289
31290
31291
31292
31293  
31294 /*
31295  * - LGPL
31296  */
31297
31298 /**
31299  * @class Roo.bootstrap.form.Markdown
31300  * @extends Roo.bootstrap.form.TextArea
31301  * Bootstrap Showdown editable area
31302  * @cfg {string} content
31303  * 
31304  * @constructor
31305  * Create a new Showdown
31306  */
31307
31308 Roo.bootstrap.form.Markdown = function(config){
31309     Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31310    
31311 };
31312
31313 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea,  {
31314     
31315     editing :false,
31316     
31317     initEvents : function()
31318     {
31319         
31320         Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31321         this.markdownEl = this.el.createChild({
31322             cls : 'roo-markdown-area'
31323         });
31324         this.inputEl().addClass('d-none');
31325         if (this.getValue() == '') {
31326             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31327             
31328         } else {
31329             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31330         }
31331         this.markdownEl.on('click', this.toggleTextEdit, this);
31332         this.on('blur', this.toggleTextEdit, this);
31333         this.on('specialkey', this.resizeTextArea, this);
31334     },
31335     
31336     toggleTextEdit : function()
31337     {
31338         var sh = this.markdownEl.getHeight();
31339         this.inputEl().addClass('d-none');
31340         this.markdownEl.addClass('d-none');
31341         if (!this.editing) {
31342             // show editor?
31343             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31344             this.inputEl().removeClass('d-none');
31345             this.inputEl().focus();
31346             this.editing = true;
31347             return;
31348         }
31349         // show showdown...
31350         this.updateMarkdown();
31351         this.markdownEl.removeClass('d-none');
31352         this.editing = false;
31353         return;
31354     },
31355     updateMarkdown : function()
31356     {
31357         if (this.getValue() == '') {
31358             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31359             return;
31360         }
31361  
31362         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31363     },
31364     
31365     resizeTextArea: function () {
31366         
31367         var sh = 100;
31368         Roo.log([sh, this.getValue().split("\n").length * 30]);
31369         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31370     },
31371     setValue : function(val)
31372     {
31373         Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31374         if (!this.editing) {
31375             this.updateMarkdown();
31376         }
31377         
31378     },
31379     focus : function()
31380     {
31381         if (!this.editing) {
31382             this.toggleTextEdit();
31383         }
31384         
31385     }
31386
31387
31388 });/*
31389  * Based on:
31390  * Ext JS Library 1.1.1
31391  * Copyright(c) 2006-2007, Ext JS, LLC.
31392  *
31393  * Originally Released Under LGPL - original licence link has changed is not relivant.
31394  *
31395  * Fork - LGPL
31396  * <script type="text/javascript">
31397  */
31398  
31399 /**
31400  * @class Roo.bootstrap.PagingToolbar
31401  * @extends Roo.bootstrap.nav.Simplebar
31402  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31403  * @constructor
31404  * Create a new PagingToolbar
31405  * @param {Object} config The config object
31406  * @param {Roo.data.Store} store
31407  */
31408 Roo.bootstrap.PagingToolbar = function(config)
31409 {
31410     // old args format still supported... - xtype is prefered..
31411         // created from xtype...
31412     
31413     this.ds = config.dataSource;
31414     
31415     if (config.store && !this.ds) {
31416         this.store= Roo.factory(config.store, Roo.data);
31417         this.ds = this.store;
31418         this.ds.xmodule = this.xmodule || false;
31419     }
31420     
31421     this.toolbarItems = [];
31422     if (config.items) {
31423         this.toolbarItems = config.items;
31424     }
31425     
31426     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31427     
31428     this.cursor = 0;
31429     
31430     if (this.ds) { 
31431         this.bind(this.ds);
31432     }
31433     
31434     if (Roo.bootstrap.version == 4) {
31435         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31436     } else {
31437         this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31438     }
31439     
31440 };
31441
31442 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31443     /**
31444      * @cfg {Roo.bootstrap.Button} buttons[]
31445      * Buttons for the toolbar
31446      */
31447      /**
31448      * @cfg {Roo.data.Store} store
31449      * The underlying data store providing the paged data
31450      */
31451     /**
31452      * @cfg {String/HTMLElement/Element} container
31453      * container The id or element that will contain the toolbar
31454      */
31455     /**
31456      * @cfg {Boolean} displayInfo
31457      * True to display the displayMsg (defaults to false)
31458      */
31459     /**
31460      * @cfg {Number} pageSize
31461      * The number of records to display per page (defaults to 20)
31462      */
31463     pageSize: 20,
31464     /**
31465      * @cfg {String} displayMsg
31466      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31467      */
31468     displayMsg : 'Displaying {0} - {1} of {2}',
31469     /**
31470      * @cfg {String} emptyMsg
31471      * The message to display when no records are found (defaults to "No data to display")
31472      */
31473     emptyMsg : 'No data to display',
31474     /**
31475      * Customizable piece of the default paging text (defaults to "Page")
31476      * @type String
31477      */
31478     beforePageText : "Page",
31479     /**
31480      * Customizable piece of the default paging text (defaults to "of %0")
31481      * @type String
31482      */
31483     afterPageText : "of {0}",
31484     /**
31485      * Customizable piece of the default paging text (defaults to "First Page")
31486      * @type String
31487      */
31488     firstText : "First Page",
31489     /**
31490      * Customizable piece of the default paging text (defaults to "Previous Page")
31491      * @type String
31492      */
31493     prevText : "Previous Page",
31494     /**
31495      * Customizable piece of the default paging text (defaults to "Next Page")
31496      * @type String
31497      */
31498     nextText : "Next Page",
31499     /**
31500      * Customizable piece of the default paging text (defaults to "Last Page")
31501      * @type String
31502      */
31503     lastText : "Last Page",
31504     /**
31505      * Customizable piece of the default paging text (defaults to "Refresh")
31506      * @type String
31507      */
31508     refreshText : "Refresh",
31509
31510     buttons : false,
31511     // private
31512     onRender : function(ct, position) 
31513     {
31514         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31515         this.navgroup.parentId = this.id;
31516         this.navgroup.onRender(this.el, null);
31517         // add the buttons to the navgroup
31518         
31519         if(this.displayInfo){
31520             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31521             this.displayEl = this.el.select('.x-paging-info', true).first();
31522 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31523 //            this.displayEl = navel.el.select('span',true).first();
31524         }
31525         
31526         var _this = this;
31527         
31528         if(this.buttons){
31529             Roo.each(_this.buttons, function(e){ // this might need to use render????
31530                Roo.factory(e).render(_this.el);
31531             });
31532         }
31533             
31534         Roo.each(_this.toolbarItems, function(e) {
31535             _this.navgroup.addItem(e);
31536         });
31537         
31538         
31539         this.first = this.navgroup.addItem({
31540             tooltip: this.firstText,
31541             cls: "prev btn-outline-secondary",
31542             html : ' <i class="fa fa-step-backward"></i>',
31543             disabled: true,
31544             preventDefault: true,
31545             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31546         });
31547         
31548         this.prev =  this.navgroup.addItem({
31549             tooltip: this.prevText,
31550             cls: "prev btn-outline-secondary",
31551             html : ' <i class="fa fa-backward"></i>',
31552             disabled: true,
31553             preventDefault: true,
31554             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
31555         });
31556     //this.addSeparator();
31557         
31558         
31559         var field = this.navgroup.addItem( {
31560             tagtype : 'span',
31561             cls : 'x-paging-position  btn-outline-secondary',
31562              disabled: true,
31563             html : this.beforePageText  +
31564                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31565                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
31566          } ); //?? escaped?
31567         
31568         this.field = field.el.select('input', true).first();
31569         this.field.on("keydown", this.onPagingKeydown, this);
31570         this.field.on("focus", function(){this.dom.select();});
31571     
31572     
31573         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
31574         //this.field.setHeight(18);
31575         //this.addSeparator();
31576         this.next = this.navgroup.addItem({
31577             tooltip: this.nextText,
31578             cls: "next btn-outline-secondary",
31579             html : ' <i class="fa fa-forward"></i>',
31580             disabled: true,
31581             preventDefault: true,
31582             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
31583         });
31584         this.last = this.navgroup.addItem({
31585             tooltip: this.lastText,
31586             html : ' <i class="fa fa-step-forward"></i>',
31587             cls: "next btn-outline-secondary",
31588             disabled: true,
31589             preventDefault: true,
31590             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
31591         });
31592     //this.addSeparator();
31593         this.loading = this.navgroup.addItem({
31594             tooltip: this.refreshText,
31595             cls: "btn-outline-secondary",
31596             html : ' <i class="fa fa-refresh"></i>',
31597             preventDefault: true,
31598             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31599         });
31600         
31601     },
31602
31603     // private
31604     updateInfo : function(){
31605         if(this.displayEl){
31606             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31607             var msg = count == 0 ?
31608                 this.emptyMsg :
31609                 String.format(
31610                     this.displayMsg,
31611                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31612                 );
31613             this.displayEl.update(msg);
31614         }
31615     },
31616
31617     // private
31618     onLoad : function(ds, r, o)
31619     {
31620         this.cursor = o.params && o.params.start ? o.params.start : 0;
31621         
31622         var d = this.getPageData(),
31623             ap = d.activePage,
31624             ps = d.pages;
31625         
31626         
31627         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31628         this.field.dom.value = ap;
31629         this.first.setDisabled(ap == 1);
31630         this.prev.setDisabled(ap == 1);
31631         this.next.setDisabled(ap == ps);
31632         this.last.setDisabled(ap == ps);
31633         this.loading.enable();
31634         this.updateInfo();
31635     },
31636
31637     // private
31638     getPageData : function(){
31639         var total = this.ds.getTotalCount();
31640         return {
31641             total : total,
31642             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31643             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31644         };
31645     },
31646
31647     // private
31648     onLoadError : function(proxy, o){
31649         this.loading.enable();
31650         if (this.ds.events.loadexception.listeners.length  < 2) {
31651             // nothing has been assigned to loadexception except this...
31652             // so 
31653             Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31654
31655         }
31656     },
31657
31658     // private
31659     onPagingKeydown : function(e){
31660         var k = e.getKey();
31661         var d = this.getPageData();
31662         if(k == e.RETURN){
31663             var v = this.field.dom.value, pageNum;
31664             if(!v || isNaN(pageNum = parseInt(v, 10))){
31665                 this.field.dom.value = d.activePage;
31666                 return;
31667             }
31668             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31669             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31670             e.stopEvent();
31671         }
31672         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))
31673         {
31674           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31675           this.field.dom.value = pageNum;
31676           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31677           e.stopEvent();
31678         }
31679         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31680         {
31681           var v = this.field.dom.value, pageNum; 
31682           var increment = (e.shiftKey) ? 10 : 1;
31683           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31684                 increment *= -1;
31685           }
31686           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31687             this.field.dom.value = d.activePage;
31688             return;
31689           }
31690           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31691           {
31692             this.field.dom.value = parseInt(v, 10) + increment;
31693             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31694             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31695           }
31696           e.stopEvent();
31697         }
31698     },
31699
31700     // private
31701     beforeLoad : function(){
31702         if(this.loading){
31703             this.loading.disable();
31704         }
31705     },
31706
31707     // private
31708     onClick : function(which){
31709         
31710         var ds = this.ds;
31711         if (!ds) {
31712             return;
31713         }
31714         
31715         switch(which){
31716             case "first":
31717                 ds.load({params:{start: 0, limit: this.pageSize}});
31718             break;
31719             case "prev":
31720                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31721             break;
31722             case "next":
31723                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31724             break;
31725             case "last":
31726                 var total = ds.getTotalCount();
31727                 var extra = total % this.pageSize;
31728                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31729                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31730             break;
31731             case "refresh":
31732                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31733             break;
31734         }
31735     },
31736
31737     /**
31738      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31739      * @param {Roo.data.Store} store The data store to unbind
31740      */
31741     unbind : function(ds){
31742         ds.un("beforeload", this.beforeLoad, this);
31743         ds.un("load", this.onLoad, this);
31744         ds.un("loadexception", this.onLoadError, this);
31745         ds.un("remove", this.updateInfo, this);
31746         ds.un("add", this.updateInfo, this);
31747         this.ds = undefined;
31748     },
31749
31750     /**
31751      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31752      * @param {Roo.data.Store} store The data store to bind
31753      */
31754     bind : function(ds){
31755         ds.on("beforeload", this.beforeLoad, this);
31756         ds.on("load", this.onLoad, this);
31757         ds.on("loadexception", this.onLoadError, this);
31758         ds.on("remove", this.updateInfo, this);
31759         ds.on("add", this.updateInfo, this);
31760         this.ds = ds;
31761     }
31762 });/*
31763  * - LGPL
31764  *
31765  * element
31766  * 
31767  */
31768
31769 /**
31770  * @class Roo.bootstrap.MessageBar
31771  * @extends Roo.bootstrap.Component
31772  * Bootstrap MessageBar class
31773  * @cfg {String} html contents of the MessageBar
31774  * @cfg {String} weight (info | success | warning | danger) default info
31775  * @cfg {String} beforeClass insert the bar before the given class
31776  * @cfg {Boolean} closable (true | false) default false
31777  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31778  * 
31779  * @constructor
31780  * Create a new Element
31781  * @param {Object} config The config object
31782  */
31783
31784 Roo.bootstrap.MessageBar = function(config){
31785     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31786 };
31787
31788 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
31789     
31790     html: '',
31791     weight: 'info',
31792     closable: false,
31793     fixed: false,
31794     beforeClass: 'bootstrap-sticky-wrap',
31795     
31796     getAutoCreate : function(){
31797         
31798         var cfg = {
31799             tag: 'div',
31800             cls: 'alert alert-dismissable alert-' + this.weight,
31801             cn: [
31802                 {
31803                     tag: 'span',
31804                     cls: 'message',
31805                     html: this.html || ''
31806                 }
31807             ]
31808         };
31809         
31810         if(this.fixed){
31811             cfg.cls += ' alert-messages-fixed';
31812         }
31813         
31814         if(this.closable){
31815             cfg.cn.push({
31816                 tag: 'button',
31817                 cls: 'close',
31818                 html: 'x'
31819             });
31820         }
31821         
31822         return cfg;
31823     },
31824     
31825     onRender : function(ct, position)
31826     {
31827         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31828         
31829         if(!this.el){
31830             var cfg = Roo.apply({},  this.getAutoCreate());
31831             cfg.id = Roo.id();
31832             
31833             if (this.cls) {
31834                 cfg.cls += ' ' + this.cls;
31835             }
31836             if (this.style) {
31837                 cfg.style = this.style;
31838             }
31839             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31840             
31841             this.el.setVisibilityMode(Roo.Element.DISPLAY);
31842         }
31843         
31844         this.el.select('>button.close').on('click', this.hide, this);
31845         
31846     },
31847     
31848     show : function()
31849     {
31850         if (!this.rendered) {
31851             this.render();
31852         }
31853         
31854         this.el.show();
31855         
31856         this.fireEvent('show', this);
31857         
31858     },
31859     
31860     hide : function()
31861     {
31862         if (!this.rendered) {
31863             this.render();
31864         }
31865         
31866         this.el.hide();
31867         
31868         this.fireEvent('hide', this);
31869     },
31870     
31871     update : function()
31872     {
31873 //        var e = this.el.dom.firstChild;
31874 //        
31875 //        if(this.closable){
31876 //            e = e.nextSibling;
31877 //        }
31878 //        
31879 //        e.data = this.html || '';
31880
31881         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31882     }
31883    
31884 });
31885
31886  
31887
31888      /*
31889  * - LGPL
31890  *
31891  * Graph
31892  * 
31893  */
31894
31895
31896 /**
31897  * @class Roo.bootstrap.Graph
31898  * @extends Roo.bootstrap.Component
31899  * Bootstrap Graph class
31900 > Prameters
31901  -sm {number} sm 4
31902  -md {number} md 5
31903  @cfg {String} graphtype  bar | vbar | pie
31904  @cfg {number} g_x coodinator | centre x (pie)
31905  @cfg {number} g_y coodinator | centre y (pie)
31906  @cfg {number} g_r radius (pie)
31907  @cfg {number} g_height height of the chart (respected by all elements in the set)
31908  @cfg {number} g_width width of the chart (respected by all elements in the set)
31909  @cfg {Object} title The title of the chart
31910     
31911  -{Array}  values
31912  -opts (object) options for the chart 
31913      o {
31914      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31915      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31916      o vgutter (number)
31917      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.
31918      o stacked (boolean) whether or not to tread values as in a stacked bar chart
31919      o to
31920      o stretch (boolean)
31921      o }
31922  -opts (object) options for the pie
31923      o{
31924      o cut
31925      o startAngle (number)
31926      o endAngle (number)
31927      } 
31928  *
31929  * @constructor
31930  * Create a new Input
31931  * @param {Object} config The config object
31932  */
31933
31934 Roo.bootstrap.Graph = function(config){
31935     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31936     
31937     this.addEvents({
31938         // img events
31939         /**
31940          * @event click
31941          * The img click event for the img.
31942          * @param {Roo.EventObject} e
31943          */
31944         "click" : true
31945     });
31946 };
31947
31948 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
31949     
31950     sm: 4,
31951     md: 5,
31952     graphtype: 'bar',
31953     g_height: 250,
31954     g_width: 400,
31955     g_x: 50,
31956     g_y: 50,
31957     g_r: 30,
31958     opts:{
31959         //g_colors: this.colors,
31960         g_type: 'soft',
31961         g_gutter: '20%'
31962
31963     },
31964     title : false,
31965
31966     getAutoCreate : function(){
31967         
31968         var cfg = {
31969             tag: 'div',
31970             html : null
31971         };
31972         
31973         
31974         return  cfg;
31975     },
31976
31977     onRender : function(ct,position){
31978         
31979         
31980         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31981         
31982         if (typeof(Raphael) == 'undefined') {
31983             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31984             return;
31985         }
31986         
31987         this.raphael = Raphael(this.el.dom);
31988         
31989                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31990                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31991                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31992                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31993                 /*
31994                 r.text(160, 10, "Single Series Chart").attr(txtattr);
31995                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31996                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31997                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
31998                 
31999                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
32000                 r.barchart(330, 10, 300, 220, data1);
32001                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
32002                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
32003                 */
32004                 
32005                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32006                 // r.barchart(30, 30, 560, 250,  xdata, {
32007                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
32008                 //     axis : "0 0 1 1",
32009                 //     axisxlabels :  xdata
32010                 //     //yvalues : cols,
32011                    
32012                 // });
32013 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
32014 //        
32015 //        this.load(null,xdata,{
32016 //                axis : "0 0 1 1",
32017 //                axisxlabels :  xdata
32018 //                });
32019
32020     },
32021
32022     load : function(graphtype,xdata,opts)
32023     {
32024         this.raphael.clear();
32025         if(!graphtype) {
32026             graphtype = this.graphtype;
32027         }
32028         if(!opts){
32029             opts = this.opts;
32030         }
32031         var r = this.raphael,
32032             fin = function () {
32033                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
32034             },
32035             fout = function () {
32036                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
32037             },
32038             pfin = function() {
32039                 this.sector.stop();
32040                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
32041
32042                 if (this.label) {
32043                     this.label[0].stop();
32044                     this.label[0].attr({ r: 7.5 });
32045                     this.label[1].attr({ "font-weight": 800 });
32046                 }
32047             },
32048             pfout = function() {
32049                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
32050
32051                 if (this.label) {
32052                     this.label[0].animate({ r: 5 }, 500, "bounce");
32053                     this.label[1].attr({ "font-weight": 400 });
32054                 }
32055             };
32056
32057         switch(graphtype){
32058             case 'bar':
32059                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32060                 break;
32061             case 'hbar':
32062                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
32063                 break;
32064             case 'pie':
32065 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
32066 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
32067 //            
32068                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
32069                 
32070                 break;
32071
32072         }
32073         
32074         if(this.title){
32075             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
32076         }
32077         
32078     },
32079     
32080     setTitle: function(o)
32081     {
32082         this.title = o;
32083     },
32084     
32085     initEvents: function() {
32086         
32087         if(!this.href){
32088             this.el.on('click', this.onClick, this);
32089         }
32090     },
32091     
32092     onClick : function(e)
32093     {
32094         Roo.log('img onclick');
32095         this.fireEvent('click', this, e);
32096     }
32097    
32098 });
32099
32100  
32101 Roo.bootstrap.dash = {};/*
32102  * - LGPL
32103  *
32104  * numberBox
32105  * 
32106  */
32107 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32108
32109 /**
32110  * @class Roo.bootstrap.dash.NumberBox
32111  * @extends Roo.bootstrap.Component
32112  * Bootstrap NumberBox class
32113  * @cfg {String} headline Box headline
32114  * @cfg {String} content Box content
32115  * @cfg {String} icon Box icon
32116  * @cfg {String} footer Footer text
32117  * @cfg {String} fhref Footer href
32118  * 
32119  * @constructor
32120  * Create a new NumberBox
32121  * @param {Object} config The config object
32122  */
32123
32124
32125 Roo.bootstrap.dash.NumberBox = function(config){
32126     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
32127     
32128 };
32129
32130 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
32131     
32132     headline : '',
32133     content : '',
32134     icon : '',
32135     footer : '',
32136     fhref : '',
32137     ficon : '',
32138     
32139     getAutoCreate : function(){
32140         
32141         var cfg = {
32142             tag : 'div',
32143             cls : 'small-box ',
32144             cn : [
32145                 {
32146                     tag : 'div',
32147                     cls : 'inner',
32148                     cn :[
32149                         {
32150                             tag : 'h3',
32151                             cls : 'roo-headline',
32152                             html : this.headline
32153                         },
32154                         {
32155                             tag : 'p',
32156                             cls : 'roo-content',
32157                             html : this.content
32158                         }
32159                     ]
32160                 }
32161             ]
32162         };
32163         
32164         if(this.icon){
32165             cfg.cn.push({
32166                 tag : 'div',
32167                 cls : 'icon',
32168                 cn :[
32169                     {
32170                         tag : 'i',
32171                         cls : 'ion ' + this.icon
32172                     }
32173                 ]
32174             });
32175         }
32176         
32177         if(this.footer){
32178             var footer = {
32179                 tag : 'a',
32180                 cls : 'small-box-footer',
32181                 href : this.fhref || '#',
32182                 html : this.footer
32183             };
32184             
32185             cfg.cn.push(footer);
32186             
32187         }
32188         
32189         return  cfg;
32190     },
32191
32192     onRender : function(ct,position){
32193         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32194
32195
32196        
32197                 
32198     },
32199
32200     setHeadline: function (value)
32201     {
32202         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32203     },
32204     
32205     setFooter: function (value, href)
32206     {
32207         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32208         
32209         if(href){
32210             this.el.select('a.small-box-footer',true).first().attr('href', href);
32211         }
32212         
32213     },
32214
32215     setContent: function (value)
32216     {
32217         this.el.select('.roo-content',true).first().dom.innerHTML = value;
32218     },
32219
32220     initEvents: function() 
32221     {   
32222         
32223     }
32224     
32225 });
32226
32227  
32228 /*
32229  * - LGPL
32230  *
32231  * TabBox
32232  * 
32233  */
32234 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32235
32236 /**
32237  * @class Roo.bootstrap.dash.TabBox
32238  * @extends Roo.bootstrap.Component
32239  * @children Roo.bootstrap.dash.TabPane
32240  * Bootstrap TabBox class
32241  * @cfg {String} title Title of the TabBox
32242  * @cfg {String} icon Icon of the TabBox
32243  * @cfg {Boolean} showtabs (true|false) show the tabs default true
32244  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32245  * 
32246  * @constructor
32247  * Create a new TabBox
32248  * @param {Object} config The config object
32249  */
32250
32251
32252 Roo.bootstrap.dash.TabBox = function(config){
32253     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32254     this.addEvents({
32255         // raw events
32256         /**
32257          * @event addpane
32258          * When a pane is added
32259          * @param {Roo.bootstrap.dash.TabPane} pane
32260          */
32261         "addpane" : true,
32262         /**
32263          * @event activatepane
32264          * When a pane is activated
32265          * @param {Roo.bootstrap.dash.TabPane} pane
32266          */
32267         "activatepane" : true
32268         
32269          
32270     });
32271     
32272     this.panes = [];
32273 };
32274
32275 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
32276
32277     title : '',
32278     icon : false,
32279     showtabs : true,
32280     tabScrollable : false,
32281     
32282     getChildContainer : function()
32283     {
32284         return this.el.select('.tab-content', true).first();
32285     },
32286     
32287     getAutoCreate : function(){
32288         
32289         var header = {
32290             tag: 'li',
32291             cls: 'pull-left header',
32292             html: this.title,
32293             cn : []
32294         };
32295         
32296         if(this.icon){
32297             header.cn.push({
32298                 tag: 'i',
32299                 cls: 'fa ' + this.icon
32300             });
32301         }
32302         
32303         var h = {
32304             tag: 'ul',
32305             cls: 'nav nav-tabs pull-right',
32306             cn: [
32307                 header
32308             ]
32309         };
32310         
32311         if(this.tabScrollable){
32312             h = {
32313                 tag: 'div',
32314                 cls: 'tab-header',
32315                 cn: [
32316                     {
32317                         tag: 'ul',
32318                         cls: 'nav nav-tabs pull-right',
32319                         cn: [
32320                             header
32321                         ]
32322                     }
32323                 ]
32324             };
32325         }
32326         
32327         var cfg = {
32328             tag: 'div',
32329             cls: 'nav-tabs-custom',
32330             cn: [
32331                 h,
32332                 {
32333                     tag: 'div',
32334                     cls: 'tab-content no-padding',
32335                     cn: []
32336                 }
32337             ]
32338         };
32339
32340         return  cfg;
32341     },
32342     initEvents : function()
32343     {
32344         //Roo.log('add add pane handler');
32345         this.on('addpane', this.onAddPane, this);
32346     },
32347      /**
32348      * Updates the box title
32349      * @param {String} html to set the title to.
32350      */
32351     setTitle : function(value)
32352     {
32353         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32354     },
32355     onAddPane : function(pane)
32356     {
32357         this.panes.push(pane);
32358         //Roo.log('addpane');
32359         //Roo.log(pane);
32360         // tabs are rendere left to right..
32361         if(!this.showtabs){
32362             return;
32363         }
32364         
32365         var ctr = this.el.select('.nav-tabs', true).first();
32366          
32367          
32368         var existing = ctr.select('.nav-tab',true);
32369         var qty = existing.getCount();;
32370         
32371         
32372         var tab = ctr.createChild({
32373             tag : 'li',
32374             cls : 'nav-tab' + (qty ? '' : ' active'),
32375             cn : [
32376                 {
32377                     tag : 'a',
32378                     href:'#',
32379                     html : pane.title
32380                 }
32381             ]
32382         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32383         pane.tab = tab;
32384         
32385         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32386         if (!qty) {
32387             pane.el.addClass('active');
32388         }
32389         
32390                 
32391     },
32392     onTabClick : function(ev,un,ob,pane)
32393     {
32394         //Roo.log('tab - prev default');
32395         ev.preventDefault();
32396         
32397         
32398         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32399         pane.tab.addClass('active');
32400         //Roo.log(pane.title);
32401         this.getChildContainer().select('.tab-pane',true).removeClass('active');
32402         // technically we should have a deactivate event.. but maybe add later.
32403         // and it should not de-activate the selected tab...
32404         this.fireEvent('activatepane', pane);
32405         pane.el.addClass('active');
32406         pane.fireEvent('activate');
32407         
32408         
32409     },
32410     
32411     getActivePane : function()
32412     {
32413         var r = false;
32414         Roo.each(this.panes, function(p) {
32415             if(p.el.hasClass('active')){
32416                 r = p;
32417                 return false;
32418             }
32419             
32420             return;
32421         });
32422         
32423         return r;
32424     }
32425     
32426     
32427 });
32428
32429  
32430 /*
32431  * - LGPL
32432  *
32433  * Tab pane
32434  * 
32435  */
32436 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32437 /**
32438  * @class Roo.bootstrap.TabPane
32439  * @extends Roo.bootstrap.Component
32440  * @children  Roo.bootstrap.Graph Roo.bootstrap.Column
32441  * Bootstrap TabPane class
32442  * @cfg {Boolean} active (false | true) Default false
32443  * @cfg {String} title title of panel
32444
32445  * 
32446  * @constructor
32447  * Create a new TabPane
32448  * @param {Object} config The config object
32449  */
32450
32451 Roo.bootstrap.dash.TabPane = function(config){
32452     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32453     
32454     this.addEvents({
32455         // raw events
32456         /**
32457          * @event activate
32458          * When a pane is activated
32459          * @param {Roo.bootstrap.dash.TabPane} pane
32460          */
32461         "activate" : true
32462          
32463     });
32464 };
32465
32466 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
32467     
32468     active : false,
32469     title : '',
32470     
32471     // the tabBox that this is attached to.
32472     tab : false,
32473      
32474     getAutoCreate : function() 
32475     {
32476         var cfg = {
32477             tag: 'div',
32478             cls: 'tab-pane'
32479         };
32480         
32481         if(this.active){
32482             cfg.cls += ' active';
32483         }
32484         
32485         return cfg;
32486     },
32487     initEvents  : function()
32488     {
32489         //Roo.log('trigger add pane handler');
32490         this.parent().fireEvent('addpane', this)
32491     },
32492     
32493      /**
32494      * Updates the tab title 
32495      * @param {String} html to set the title to.
32496      */
32497     setTitle: function(str)
32498     {
32499         if (!this.tab) {
32500             return;
32501         }
32502         this.title = str;
32503         this.tab.select('a', true).first().dom.innerHTML = str;
32504         
32505     }
32506     
32507     
32508     
32509 });
32510
32511  
32512
32513
32514  /*
32515  * - LGPL
32516  *
32517  * Tooltip
32518  * 
32519  */
32520
32521 /**
32522  * @class Roo.bootstrap.Tooltip
32523  * Bootstrap Tooltip class
32524  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32525  * to determine which dom element triggers the tooltip.
32526  * 
32527  * It needs to add support for additional attributes like tooltip-position
32528  * 
32529  * @constructor
32530  * Create a new Toolti
32531  * @param {Object} config The config object
32532  */
32533
32534 Roo.bootstrap.Tooltip = function(config){
32535     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32536     
32537     this.alignment = Roo.bootstrap.Tooltip.alignment;
32538     
32539     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32540         this.alignment = config.alignment;
32541     }
32542     
32543 };
32544
32545 Roo.apply(Roo.bootstrap.Tooltip, {
32546     /**
32547      * @function init initialize tooltip monitoring.
32548      * @static
32549      */
32550     currentEl : false,
32551     currentTip : false,
32552     currentRegion : false,
32553     
32554     //  init : delay?
32555     
32556     init : function()
32557     {
32558         Roo.get(document).on('mouseover', this.enter ,this);
32559         Roo.get(document).on('mouseout', this.leave, this);
32560          
32561         
32562         this.currentTip = new Roo.bootstrap.Tooltip();
32563     },
32564     
32565     enter : function(ev)
32566     {
32567         var dom = ev.getTarget();
32568         
32569         //Roo.log(['enter',dom]);
32570         var el = Roo.fly(dom);
32571         if (this.currentEl) {
32572             //Roo.log(dom);
32573             //Roo.log(this.currentEl);
32574             //Roo.log(this.currentEl.contains(dom));
32575             if (this.currentEl == el) {
32576                 return;
32577             }
32578             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32579                 return;
32580             }
32581
32582         }
32583         
32584         if (this.currentTip.el) {
32585             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32586         }    
32587         //Roo.log(ev);
32588         
32589         if(!el || el.dom == document){
32590             return;
32591         }
32592         
32593         var bindEl = el; 
32594         var pel = false;
32595         if (!el.attr('tooltip')) {
32596             pel = el.findParent("[tooltip]");
32597             if (pel) {
32598                 bindEl = Roo.get(pel);
32599             }
32600         }
32601         
32602        
32603         
32604         // you can not look for children, as if el is the body.. then everythign is the child..
32605         if (!pel && !el.attr('tooltip')) { //
32606             if (!el.select("[tooltip]").elements.length) {
32607                 return;
32608             }
32609             // is the mouse over this child...?
32610             bindEl = el.select("[tooltip]").first();
32611             var xy = ev.getXY();
32612             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32613                 //Roo.log("not in region.");
32614                 return;
32615             }
32616             //Roo.log("child element over..");
32617             
32618         }
32619         this.currentEl = el;
32620         this.currentTip.bind(bindEl);
32621         this.currentRegion = Roo.lib.Region.getRegion(dom);
32622         this.currentTip.enter();
32623         
32624     },
32625     leave : function(ev)
32626     {
32627         var dom = ev.getTarget();
32628         //Roo.log(['leave',dom]);
32629         if (!this.currentEl) {
32630             return;
32631         }
32632         
32633         
32634         if (dom != this.currentEl.dom) {
32635             return;
32636         }
32637         var xy = ev.getXY();
32638         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
32639             return;
32640         }
32641         // only activate leave if mouse cursor is outside... bounding box..
32642         
32643         
32644         
32645         
32646         if (this.currentTip) {
32647             this.currentTip.leave();
32648         }
32649         //Roo.log('clear currentEl');
32650         this.currentEl = false;
32651         
32652         
32653     },
32654     alignment : {
32655         'left' : ['r-l', [-2,0], 'right'],
32656         'right' : ['l-r', [2,0], 'left'],
32657         'bottom' : ['t-b', [0,2], 'top'],
32658         'top' : [ 'b-t', [0,-2], 'bottom']
32659     }
32660     
32661 });
32662
32663
32664 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
32665     
32666     
32667     bindEl : false,
32668     
32669     delay : null, // can be { show : 300 , hide: 500}
32670     
32671     timeout : null,
32672     
32673     hoverState : null, //???
32674     
32675     placement : 'bottom', 
32676     
32677     alignment : false,
32678     
32679     getAutoCreate : function(){
32680     
32681         var cfg = {
32682            cls : 'tooltip',   
32683            role : 'tooltip',
32684            cn : [
32685                 {
32686                     cls : 'tooltip-arrow arrow'
32687                 },
32688                 {
32689                     cls : 'tooltip-inner'
32690                 }
32691            ]
32692         };
32693         
32694         return cfg;
32695     },
32696     bind : function(el)
32697     {
32698         this.bindEl = el;
32699     },
32700     
32701     initEvents : function()
32702     {
32703         this.arrowEl = this.el.select('.arrow', true).first();
32704         this.innerEl = this.el.select('.tooltip-inner', true).first();
32705     },
32706     
32707     enter : function () {
32708        
32709         if (this.timeout != null) {
32710             clearTimeout(this.timeout);
32711         }
32712         
32713         this.hoverState = 'in';
32714          //Roo.log("enter - show");
32715         if (!this.delay || !this.delay.show) {
32716             this.show();
32717             return;
32718         }
32719         var _t = this;
32720         this.timeout = setTimeout(function () {
32721             if (_t.hoverState == 'in') {
32722                 _t.show();
32723             }
32724         }, this.delay.show);
32725     },
32726     leave : function()
32727     {
32728         clearTimeout(this.timeout);
32729     
32730         this.hoverState = 'out';
32731          if (!this.delay || !this.delay.hide) {
32732             this.hide();
32733             return;
32734         }
32735        
32736         var _t = this;
32737         this.timeout = setTimeout(function () {
32738             //Roo.log("leave - timeout");
32739             
32740             if (_t.hoverState == 'out') {
32741                 _t.hide();
32742                 Roo.bootstrap.Tooltip.currentEl = false;
32743             }
32744         }, delay);
32745     },
32746     
32747     show : function (msg)
32748     {
32749         if (!this.el) {
32750             this.render(document.body);
32751         }
32752         // set content.
32753         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32754         
32755         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32756         
32757         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32758         
32759         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32760                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32761
32762         this.el.addClass(this.bindEl.attr('tooltip-class'));
32763         
32764         var placement = typeof this.placement == 'function' ?
32765             this.placement.call(this, this.el, on_el) :
32766             this.placement;
32767
32768         if(this.bindEl.attr('tooltip-placement')) {
32769             placement = this.bindEl.attr('tooltip-placement');
32770         }
32771             
32772         var autoToken = /\s?auto?\s?/i;
32773         var autoPlace = autoToken.test(placement);
32774         if (autoPlace) {
32775             placement = placement.replace(autoToken, '') || 'top';
32776         }
32777         
32778         //this.el.detach()
32779         //this.el.setXY([0,0]);
32780         this.el.show();
32781         //this.el.dom.style.display='block';
32782         
32783         //this.el.appendTo(on_el);
32784         
32785         var p = this.getPosition();
32786         var box = this.el.getBox();
32787         
32788         if (autoPlace) {
32789             // fixme..
32790         }
32791         
32792         var align = this.alignment[placement];
32793         
32794         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32795         
32796         if(placement == 'top' || placement == 'bottom'){
32797             if(xy[0] < 0){
32798                 placement = 'right';
32799             }
32800             
32801             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32802                 placement = 'left';
32803             }
32804             
32805             var scroll = Roo.select('body', true).first().getScroll();
32806             
32807             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32808                 placement = 'top';
32809             }
32810             
32811             align = this.alignment[placement];
32812             
32813             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32814             
32815         }
32816         
32817         var elems = document.getElementsByTagName('div');
32818         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32819         for (var i = 0; i < elems.length; i++) {
32820           var zindex = Number.parseInt(
32821                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32822                 10
32823           );
32824           if (zindex > highest) {
32825             highest = zindex;
32826           }
32827         }
32828         
32829         
32830         
32831         this.el.dom.style.zIndex = highest;
32832         
32833         this.el.alignTo(this.bindEl, align[0],align[1]);
32834         //var arrow = this.el.select('.arrow',true).first();
32835         //arrow.set(align[2], 
32836         
32837         this.el.addClass(placement);
32838         this.el.addClass("bs-tooltip-"+ placement);
32839         
32840         this.el.addClass('in fade show');
32841         
32842         this.hoverState = null;
32843         
32844         if (this.el.hasClass('fade')) {
32845             // fade it?
32846         }
32847         
32848         
32849         
32850         
32851         
32852     },
32853     hide : function()
32854     {
32855          
32856         if (!this.el) {
32857             return;
32858         }
32859         //this.el.setXY([0,0]);
32860         this.el.removeClass(this.bindEl.attr('tooltip-class'));
32861         this.el.removeClass(['show', 'in']);
32862         //this.el.hide();
32863         
32864     }
32865     
32866 });
32867  
32868
32869  /*
32870  * - LGPL
32871  *
32872  * Location Picker
32873  * 
32874  */
32875
32876 /**
32877  * @class Roo.bootstrap.LocationPicker
32878  * @extends Roo.bootstrap.Component
32879  * Bootstrap LocationPicker class
32880  * @cfg {Number} latitude Position when init default 0
32881  * @cfg {Number} longitude Position when init default 0
32882  * @cfg {Number} zoom default 15
32883  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32884  * @cfg {Boolean} mapTypeControl default false
32885  * @cfg {Boolean} disableDoubleClickZoom default false
32886  * @cfg {Boolean} scrollwheel default true
32887  * @cfg {Boolean} streetViewControl default false
32888  * @cfg {Number} radius default 0
32889  * @cfg {String} locationName
32890  * @cfg {Boolean} draggable default true
32891  * @cfg {Boolean} enableAutocomplete default false
32892  * @cfg {Boolean} enableReverseGeocode default true
32893  * @cfg {String} markerTitle
32894  * 
32895  * @constructor
32896  * Create a new LocationPicker
32897  * @param {Object} config The config object
32898  */
32899
32900
32901 Roo.bootstrap.LocationPicker = function(config){
32902     
32903     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32904     
32905     this.addEvents({
32906         /**
32907          * @event initial
32908          * Fires when the picker initialized.
32909          * @param {Roo.bootstrap.LocationPicker} this
32910          * @param {Google Location} location
32911          */
32912         initial : true,
32913         /**
32914          * @event positionchanged
32915          * Fires when the picker position changed.
32916          * @param {Roo.bootstrap.LocationPicker} this
32917          * @param {Google Location} location
32918          */
32919         positionchanged : true,
32920         /**
32921          * @event resize
32922          * Fires when the map resize.
32923          * @param {Roo.bootstrap.LocationPicker} this
32924          */
32925         resize : true,
32926         /**
32927          * @event show
32928          * Fires when the map show.
32929          * @param {Roo.bootstrap.LocationPicker} this
32930          */
32931         show : true,
32932         /**
32933          * @event hide
32934          * Fires when the map hide.
32935          * @param {Roo.bootstrap.LocationPicker} this
32936          */
32937         hide : true,
32938         /**
32939          * @event mapClick
32940          * Fires when click the map.
32941          * @param {Roo.bootstrap.LocationPicker} this
32942          * @param {Map event} e
32943          */
32944         mapClick : true,
32945         /**
32946          * @event mapRightClick
32947          * Fires when right click the map.
32948          * @param {Roo.bootstrap.LocationPicker} this
32949          * @param {Map event} e
32950          */
32951         mapRightClick : true,
32952         /**
32953          * @event markerClick
32954          * Fires when click the marker.
32955          * @param {Roo.bootstrap.LocationPicker} this
32956          * @param {Map event} e
32957          */
32958         markerClick : true,
32959         /**
32960          * @event markerRightClick
32961          * Fires when right click the marker.
32962          * @param {Roo.bootstrap.LocationPicker} this
32963          * @param {Map event} e
32964          */
32965         markerRightClick : true,
32966         /**
32967          * @event OverlayViewDraw
32968          * Fires when OverlayView Draw
32969          * @param {Roo.bootstrap.LocationPicker} this
32970          */
32971         OverlayViewDraw : true,
32972         /**
32973          * @event OverlayViewOnAdd
32974          * Fires when OverlayView Draw
32975          * @param {Roo.bootstrap.LocationPicker} this
32976          */
32977         OverlayViewOnAdd : true,
32978         /**
32979          * @event OverlayViewOnRemove
32980          * Fires when OverlayView Draw
32981          * @param {Roo.bootstrap.LocationPicker} this
32982          */
32983         OverlayViewOnRemove : true,
32984         /**
32985          * @event OverlayViewShow
32986          * Fires when OverlayView Draw
32987          * @param {Roo.bootstrap.LocationPicker} this
32988          * @param {Pixel} cpx
32989          */
32990         OverlayViewShow : true,
32991         /**
32992          * @event OverlayViewHide
32993          * Fires when OverlayView Draw
32994          * @param {Roo.bootstrap.LocationPicker} this
32995          */
32996         OverlayViewHide : true,
32997         /**
32998          * @event loadexception
32999          * Fires when load google lib failed.
33000          * @param {Roo.bootstrap.LocationPicker} this
33001          */
33002         loadexception : true
33003     });
33004         
33005 };
33006
33007 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
33008     
33009     gMapContext: false,
33010     
33011     latitude: 0,
33012     longitude: 0,
33013     zoom: 15,
33014     mapTypeId: false,
33015     mapTypeControl: false,
33016     disableDoubleClickZoom: false,
33017     scrollwheel: true,
33018     streetViewControl: false,
33019     radius: 0,
33020     locationName: '',
33021     draggable: true,
33022     enableAutocomplete: false,
33023     enableReverseGeocode: true,
33024     markerTitle: '',
33025     
33026     getAutoCreate: function()
33027     {
33028
33029         var cfg = {
33030             tag: 'div',
33031             cls: 'roo-location-picker'
33032         };
33033         
33034         return cfg
33035     },
33036     
33037     initEvents: function(ct, position)
33038     {       
33039         if(!this.el.getWidth() || this.isApplied()){
33040             return;
33041         }
33042         
33043         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33044         
33045         this.initial();
33046     },
33047     
33048     initial: function()
33049     {
33050         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
33051             this.fireEvent('loadexception', this);
33052             return;
33053         }
33054         
33055         if(!this.mapTypeId){
33056             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
33057         }
33058         
33059         this.gMapContext = this.GMapContext();
33060         
33061         this.initOverlayView();
33062         
33063         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
33064         
33065         var _this = this;
33066                 
33067         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
33068             _this.setPosition(_this.gMapContext.marker.position);
33069         });
33070         
33071         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
33072             _this.fireEvent('mapClick', this, event);
33073             
33074         });
33075
33076         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
33077             _this.fireEvent('mapRightClick', this, event);
33078             
33079         });
33080         
33081         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
33082             _this.fireEvent('markerClick', this, event);
33083             
33084         });
33085
33086         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
33087             _this.fireEvent('markerRightClick', this, event);
33088             
33089         });
33090         
33091         this.setPosition(this.gMapContext.location);
33092         
33093         this.fireEvent('initial', this, this.gMapContext.location);
33094     },
33095     
33096     initOverlayView: function()
33097     {
33098         var _this = this;
33099         
33100         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
33101             
33102             draw: function()
33103             {
33104                 _this.fireEvent('OverlayViewDraw', _this);
33105             },
33106             
33107             onAdd: function()
33108             {
33109                 _this.fireEvent('OverlayViewOnAdd', _this);
33110             },
33111             
33112             onRemove: function()
33113             {
33114                 _this.fireEvent('OverlayViewOnRemove', _this);
33115             },
33116             
33117             show: function(cpx)
33118             {
33119                 _this.fireEvent('OverlayViewShow', _this, cpx);
33120             },
33121             
33122             hide: function()
33123             {
33124                 _this.fireEvent('OverlayViewHide', _this);
33125             }
33126             
33127         });
33128     },
33129     
33130     fromLatLngToContainerPixel: function(event)
33131     {
33132         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
33133     },
33134     
33135     isApplied: function() 
33136     {
33137         return this.getGmapContext() == false ? false : true;
33138     },
33139     
33140     getGmapContext: function() 
33141     {
33142         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
33143     },
33144     
33145     GMapContext: function() 
33146     {
33147         var position = new google.maps.LatLng(this.latitude, this.longitude);
33148         
33149         var _map = new google.maps.Map(this.el.dom, {
33150             center: position,
33151             zoom: this.zoom,
33152             mapTypeId: this.mapTypeId,
33153             mapTypeControl: this.mapTypeControl,
33154             disableDoubleClickZoom: this.disableDoubleClickZoom,
33155             scrollwheel: this.scrollwheel,
33156             streetViewControl: this.streetViewControl,
33157             locationName: this.locationName,
33158             draggable: this.draggable,
33159             enableAutocomplete: this.enableAutocomplete,
33160             enableReverseGeocode: this.enableReverseGeocode
33161         });
33162         
33163         var _marker = new google.maps.Marker({
33164             position: position,
33165             map: _map,
33166             title: this.markerTitle,
33167             draggable: this.draggable
33168         });
33169         
33170         return {
33171             map: _map,
33172             marker: _marker,
33173             circle: null,
33174             location: position,
33175             radius: this.radius,
33176             locationName: this.locationName,
33177             addressComponents: {
33178                 formatted_address: null,
33179                 addressLine1: null,
33180                 addressLine2: null,
33181                 streetName: null,
33182                 streetNumber: null,
33183                 city: null,
33184                 district: null,
33185                 state: null,
33186                 stateOrProvince: null
33187             },
33188             settings: this,
33189             domContainer: this.el.dom,
33190             geodecoder: new google.maps.Geocoder()
33191         };
33192     },
33193     
33194     drawCircle: function(center, radius, options) 
33195     {
33196         if (this.gMapContext.circle != null) {
33197             this.gMapContext.circle.setMap(null);
33198         }
33199         if (radius > 0) {
33200             radius *= 1;
33201             options = Roo.apply({}, options, {
33202                 strokeColor: "#0000FF",
33203                 strokeOpacity: .35,
33204                 strokeWeight: 2,
33205                 fillColor: "#0000FF",
33206                 fillOpacity: .2
33207             });
33208             
33209             options.map = this.gMapContext.map;
33210             options.radius = radius;
33211             options.center = center;
33212             this.gMapContext.circle = new google.maps.Circle(options);
33213             return this.gMapContext.circle;
33214         }
33215         
33216         return null;
33217     },
33218     
33219     setPosition: function(location) 
33220     {
33221         this.gMapContext.location = location;
33222         this.gMapContext.marker.setPosition(location);
33223         this.gMapContext.map.panTo(location);
33224         this.drawCircle(location, this.gMapContext.radius, {});
33225         
33226         var _this = this;
33227         
33228         if (this.gMapContext.settings.enableReverseGeocode) {
33229             this.gMapContext.geodecoder.geocode({
33230                 latLng: this.gMapContext.location
33231             }, function(results, status) {
33232                 
33233                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33234                     _this.gMapContext.locationName = results[0].formatted_address;
33235                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33236                     
33237                     _this.fireEvent('positionchanged', this, location);
33238                 }
33239             });
33240             
33241             return;
33242         }
33243         
33244         this.fireEvent('positionchanged', this, location);
33245     },
33246     
33247     resize: function()
33248     {
33249         google.maps.event.trigger(this.gMapContext.map, "resize");
33250         
33251         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33252         
33253         this.fireEvent('resize', this);
33254     },
33255     
33256     setPositionByLatLng: function(latitude, longitude)
33257     {
33258         this.setPosition(new google.maps.LatLng(latitude, longitude));
33259     },
33260     
33261     getCurrentPosition: function() 
33262     {
33263         return {
33264             latitude: this.gMapContext.location.lat(),
33265             longitude: this.gMapContext.location.lng()
33266         };
33267     },
33268     
33269     getAddressName: function() 
33270     {
33271         return this.gMapContext.locationName;
33272     },
33273     
33274     getAddressComponents: function() 
33275     {
33276         return this.gMapContext.addressComponents;
33277     },
33278     
33279     address_component_from_google_geocode: function(address_components) 
33280     {
33281         var result = {};
33282         
33283         for (var i = 0; i < address_components.length; i++) {
33284             var component = address_components[i];
33285             if (component.types.indexOf("postal_code") >= 0) {
33286                 result.postalCode = component.short_name;
33287             } else if (component.types.indexOf("street_number") >= 0) {
33288                 result.streetNumber = component.short_name;
33289             } else if (component.types.indexOf("route") >= 0) {
33290                 result.streetName = component.short_name;
33291             } else if (component.types.indexOf("neighborhood") >= 0) {
33292                 result.city = component.short_name;
33293             } else if (component.types.indexOf("locality") >= 0) {
33294                 result.city = component.short_name;
33295             } else if (component.types.indexOf("sublocality") >= 0) {
33296                 result.district = component.short_name;
33297             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33298                 result.stateOrProvince = component.short_name;
33299             } else if (component.types.indexOf("country") >= 0) {
33300                 result.country = component.short_name;
33301             }
33302         }
33303         
33304         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33305         result.addressLine2 = "";
33306         return result;
33307     },
33308     
33309     setZoomLevel: function(zoom)
33310     {
33311         this.gMapContext.map.setZoom(zoom);
33312     },
33313     
33314     show: function()
33315     {
33316         if(!this.el){
33317             return;
33318         }
33319         
33320         this.el.show();
33321         
33322         this.resize();
33323         
33324         this.fireEvent('show', this);
33325     },
33326     
33327     hide: function()
33328     {
33329         if(!this.el){
33330             return;
33331         }
33332         
33333         this.el.hide();
33334         
33335         this.fireEvent('hide', this);
33336     }
33337     
33338 });
33339
33340 Roo.apply(Roo.bootstrap.LocationPicker, {
33341     
33342     OverlayView : function(map, options)
33343     {
33344         options = options || {};
33345         
33346         this.setMap(map);
33347     }
33348     
33349     
33350 });/**
33351  * @class Roo.bootstrap.Alert
33352  * @extends Roo.bootstrap.Component
33353  * Bootstrap Alert class - shows an alert area box
33354  * eg
33355  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33356   Enter a valid email address
33357 </div>
33358  * @licence LGPL
33359  * @cfg {String} title The title of alert
33360  * @cfg {String} html The content of alert
33361  * @cfg {String} weight (success|info|warning|danger) Weight of the message
33362  * @cfg {String} fa font-awesomeicon
33363  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33364  * @cfg {Boolean} close true to show a x closer
33365  * 
33366  * 
33367  * @constructor
33368  * Create a new alert
33369  * @param {Object} config The config object
33370  */
33371
33372
33373 Roo.bootstrap.Alert = function(config){
33374     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33375     
33376 };
33377
33378 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
33379     
33380     title: '',
33381     html: '',
33382     weight: false,
33383     fa: false,
33384     faicon: false, // BC
33385     close : false,
33386     
33387     
33388     getAutoCreate : function()
33389     {
33390         
33391         var cfg = {
33392             tag : 'div',
33393             cls : 'alert',
33394             cn : [
33395                 {
33396                     tag: 'button',
33397                     type :  "button",
33398                     cls: "close",
33399                     html : '×',
33400                     style : this.close ? '' : 'display:none'
33401                 },
33402                 {
33403                     tag : 'i',
33404                     cls : 'roo-alert-icon'
33405                     
33406                 },
33407                 {
33408                     tag : 'b',
33409                     cls : 'roo-alert-title',
33410                     html : this.title
33411                 },
33412                 {
33413                     tag : 'span',
33414                     cls : 'roo-alert-text',
33415                     html : this.html
33416                 }
33417             ]
33418         };
33419         
33420         if(this.faicon){
33421             cfg.cn[0].cls += ' fa ' + this.faicon;
33422         }
33423         if(this.fa){
33424             cfg.cn[0].cls += ' fa ' + this.fa;
33425         }
33426         
33427         if(this.weight){
33428             cfg.cls += ' alert-' + this.weight;
33429         }
33430         
33431         return cfg;
33432     },
33433     
33434     initEvents: function() 
33435     {
33436         this.el.setVisibilityMode(Roo.Element.DISPLAY);
33437         this.titleEl =  this.el.select('.roo-alert-title',true).first();
33438         this.iconEl = this.el.select('.roo-alert-icon',true).first();
33439         this.htmlEl = this.el.select('.roo-alert-text',true).first();
33440         if (this.seconds > 0) {
33441             this.hide.defer(this.seconds, this);
33442         }
33443     },
33444     /**
33445      * Set the Title Message HTML
33446      * @param {String} html
33447      */
33448     setTitle : function(str)
33449     {
33450         this.titleEl.dom.innerHTML = str;
33451     },
33452      
33453      /**
33454      * Set the Body Message HTML
33455      * @param {String} html
33456      */
33457     setHtml : function(str)
33458     {
33459         this.htmlEl.dom.innerHTML = str;
33460     },
33461     /**
33462      * Set the Weight of the alert
33463      * @param {String} (success|info|warning|danger) weight
33464      */
33465     
33466     setWeight : function(weight)
33467     {
33468         if(this.weight){
33469             this.el.removeClass('alert-' + this.weight);
33470         }
33471         
33472         this.weight = weight;
33473         
33474         this.el.addClass('alert-' + this.weight);
33475     },
33476       /**
33477      * Set the Icon of the alert
33478      * @param {String} see fontawsome names (name without the 'fa-' bit)
33479      */
33480     setIcon : function(icon)
33481     {
33482         if(this.faicon){
33483             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33484         }
33485         
33486         this.faicon = icon;
33487         
33488         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33489     },
33490     /**
33491      * Hide the Alert
33492      */
33493     hide: function() 
33494     {
33495         this.el.hide();   
33496     },
33497     /**
33498      * Show the Alert
33499      */
33500     show: function() 
33501     {  
33502         this.el.show();   
33503     }
33504     
33505 });
33506
33507  
33508 /*
33509 * Licence: LGPL
33510 */
33511
33512 /**
33513  * @class Roo.bootstrap.UploadCropbox
33514  * @extends Roo.bootstrap.Component
33515  * Bootstrap UploadCropbox class
33516  * @cfg {String} emptyText show when image has been loaded
33517  * @cfg {String} rotateNotify show when image too small to rotate
33518  * @cfg {Number} errorTimeout default 3000
33519  * @cfg {Number} minWidth default 300
33520  * @cfg {Number} minHeight default 300
33521  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33522  * @cfg {Boolean} isDocument (true|false) default false
33523  * @cfg {String} url action url
33524  * @cfg {String} paramName default 'imageUpload'
33525  * @cfg {String} method default POST
33526  * @cfg {Boolean} loadMask (true|false) default true
33527  * @cfg {Boolean} loadingText default 'Loading...'
33528  * 
33529  * @constructor
33530  * Create a new UploadCropbox
33531  * @param {Object} config The config object
33532  */
33533
33534 Roo.bootstrap.UploadCropbox = function(config){
33535     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33536     
33537     this.addEvents({
33538         /**
33539          * @event beforeselectfile
33540          * Fire before select file
33541          * @param {Roo.bootstrap.UploadCropbox} this
33542          */
33543         "beforeselectfile" : true,
33544         /**
33545          * @event initial
33546          * Fire after initEvent
33547          * @param {Roo.bootstrap.UploadCropbox} this
33548          */
33549         "initial" : true,
33550         /**
33551          * @event crop
33552          * Fire after initEvent
33553          * @param {Roo.bootstrap.UploadCropbox} this
33554          * @param {String} data
33555          */
33556         "crop" : true,
33557         /**
33558          * @event prepare
33559          * Fire when preparing the file data
33560          * @param {Roo.bootstrap.UploadCropbox} this
33561          * @param {Object} file
33562          */
33563         "prepare" : true,
33564         /**
33565          * @event exception
33566          * Fire when get exception
33567          * @param {Roo.bootstrap.UploadCropbox} this
33568          * @param {XMLHttpRequest} xhr
33569          */
33570         "exception" : true,
33571         /**
33572          * @event beforeloadcanvas
33573          * Fire before load the canvas
33574          * @param {Roo.bootstrap.UploadCropbox} this
33575          * @param {String} src
33576          */
33577         "beforeloadcanvas" : true,
33578         /**
33579          * @event trash
33580          * Fire when trash image
33581          * @param {Roo.bootstrap.UploadCropbox} this
33582          */
33583         "trash" : true,
33584         /**
33585          * @event download
33586          * Fire when download the image
33587          * @param {Roo.bootstrap.UploadCropbox} this
33588          */
33589         "download" : true,
33590         /**
33591          * @event footerbuttonclick
33592          * Fire when footerbuttonclick
33593          * @param {Roo.bootstrap.UploadCropbox} this
33594          * @param {String} type
33595          */
33596         "footerbuttonclick" : true,
33597         /**
33598          * @event resize
33599          * Fire when resize
33600          * @param {Roo.bootstrap.UploadCropbox} this
33601          */
33602         "resize" : true,
33603         /**
33604          * @event rotate
33605          * Fire when rotate the image
33606          * @param {Roo.bootstrap.UploadCropbox} this
33607          * @param {String} pos
33608          */
33609         "rotate" : true,
33610         /**
33611          * @event inspect
33612          * Fire when inspect the file
33613          * @param {Roo.bootstrap.UploadCropbox} this
33614          * @param {Object} file
33615          */
33616         "inspect" : true,
33617         /**
33618          * @event upload
33619          * Fire when xhr upload the file
33620          * @param {Roo.bootstrap.UploadCropbox} this
33621          * @param {Object} data
33622          */
33623         "upload" : true,
33624         /**
33625          * @event arrange
33626          * Fire when arrange the file data
33627          * @param {Roo.bootstrap.UploadCropbox} this
33628          * @param {Object} formData
33629          */
33630         "arrange" : true
33631     });
33632     
33633     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33634 };
33635
33636 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
33637     
33638     emptyText : 'Click to upload image',
33639     rotateNotify : 'Image is too small to rotate',
33640     errorTimeout : 3000,
33641     scale : 0,
33642     baseScale : 1,
33643     rotate : 0,
33644     dragable : false,
33645     pinching : false,
33646     mouseX : 0,
33647     mouseY : 0,
33648     cropData : false,
33649     minWidth : 300,
33650     minHeight : 300,
33651     file : false,
33652     exif : {},
33653     baseRotate : 1,
33654     cropType : 'image/jpeg',
33655     buttons : false,
33656     canvasLoaded : false,
33657     isDocument : false,
33658     method : 'POST',
33659     paramName : 'imageUpload',
33660     loadMask : true,
33661     loadingText : 'Loading...',
33662     maskEl : false,
33663     
33664     getAutoCreate : function()
33665     {
33666         var cfg = {
33667             tag : 'div',
33668             cls : 'roo-upload-cropbox',
33669             cn : [
33670                 {
33671                     tag : 'input',
33672                     cls : 'roo-upload-cropbox-selector',
33673                     type : 'file'
33674                 },
33675                 {
33676                     tag : 'div',
33677                     cls : 'roo-upload-cropbox-body',
33678                     style : 'cursor:pointer',
33679                     cn : [
33680                         {
33681                             tag : 'div',
33682                             cls : 'roo-upload-cropbox-preview'
33683                         },
33684                         {
33685                             tag : 'div',
33686                             cls : 'roo-upload-cropbox-thumb'
33687                         },
33688                         {
33689                             tag : 'div',
33690                             cls : 'roo-upload-cropbox-empty-notify',
33691                             html : this.emptyText
33692                         },
33693                         {
33694                             tag : 'div',
33695                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33696                             html : this.rotateNotify
33697                         }
33698                     ]
33699                 },
33700                 {
33701                     tag : 'div',
33702                     cls : 'roo-upload-cropbox-footer',
33703                     cn : {
33704                         tag : 'div',
33705                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33706                         cn : []
33707                     }
33708                 }
33709             ]
33710         };
33711         
33712         return cfg;
33713     },
33714     
33715     onRender : function(ct, position)
33716     {
33717         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33718         
33719         if (this.buttons.length) {
33720             
33721             Roo.each(this.buttons, function(bb) {
33722                 
33723                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33724                 
33725                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33726                 
33727             }, this);
33728         }
33729         
33730         if(this.loadMask){
33731             this.maskEl = this.el;
33732         }
33733     },
33734     
33735     initEvents : function()
33736     {
33737         this.urlAPI = (window.createObjectURL && window) || 
33738                                 (window.URL && URL.revokeObjectURL && URL) || 
33739                                 (window.webkitURL && webkitURL);
33740                         
33741         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33742         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33743         
33744         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33745         this.selectorEl.hide();
33746         
33747         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33748         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33749         
33750         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33751         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33752         this.thumbEl.hide();
33753         
33754         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33755         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33756         
33757         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33758         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33759         this.errorEl.hide();
33760         
33761         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33762         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33763         this.footerEl.hide();
33764         
33765         this.setThumbBoxSize();
33766         
33767         this.bind();
33768         
33769         this.resize();
33770         
33771         this.fireEvent('initial', this);
33772     },
33773
33774     bind : function()
33775     {
33776         var _this = this;
33777         
33778         window.addEventListener("resize", function() { _this.resize(); } );
33779         
33780         this.bodyEl.on('click', this.beforeSelectFile, this);
33781         
33782         if(Roo.isTouch){
33783             this.bodyEl.on('touchstart', this.onTouchStart, this);
33784             this.bodyEl.on('touchmove', this.onTouchMove, this);
33785             this.bodyEl.on('touchend', this.onTouchEnd, this);
33786         }
33787         
33788         if(!Roo.isTouch){
33789             this.bodyEl.on('mousedown', this.onMouseDown, this);
33790             this.bodyEl.on('mousemove', this.onMouseMove, this);
33791             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33792             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33793             Roo.get(document).on('mouseup', this.onMouseUp, this);
33794         }
33795         
33796         this.selectorEl.on('change', this.onFileSelected, this);
33797     },
33798     
33799     reset : function()
33800     {    
33801         this.scale = 0;
33802         this.baseScale = 1;
33803         this.rotate = 0;
33804         this.baseRotate = 1;
33805         this.dragable = false;
33806         this.pinching = false;
33807         this.mouseX = 0;
33808         this.mouseY = 0;
33809         this.cropData = false;
33810         this.notifyEl.dom.innerHTML = this.emptyText;
33811         
33812         this.selectorEl.dom.value = '';
33813         
33814     },
33815     
33816     resize : function()
33817     {
33818         if(this.fireEvent('resize', this) != false){
33819             this.setThumbBoxPosition();
33820             this.setCanvasPosition();
33821         }
33822     },
33823     
33824     onFooterButtonClick : function(e, el, o, type)
33825     {
33826         switch (type) {
33827             case 'rotate-left' :
33828                 this.onRotateLeft(e);
33829                 break;
33830             case 'rotate-right' :
33831                 this.onRotateRight(e);
33832                 break;
33833             case 'picture' :
33834                 this.beforeSelectFile(e);
33835                 break;
33836             case 'trash' :
33837                 this.trash(e);
33838                 break;
33839             case 'crop' :
33840                 this.crop(e);
33841                 break;
33842             case 'download' :
33843                 this.download(e);
33844                 break;
33845             default :
33846                 break;
33847         }
33848         
33849         this.fireEvent('footerbuttonclick', this, type);
33850     },
33851     
33852     beforeSelectFile : function(e)
33853     {
33854         e.preventDefault();
33855         
33856         if(this.fireEvent('beforeselectfile', this) != false){
33857             this.selectorEl.dom.click();
33858         }
33859     },
33860     
33861     onFileSelected : function(e)
33862     {
33863         e.preventDefault();
33864         
33865         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33866             return;
33867         }
33868         
33869         var file = this.selectorEl.dom.files[0];
33870         
33871         if(this.fireEvent('inspect', this, file) != false){
33872             this.prepare(file);
33873         }
33874         
33875     },
33876     
33877     trash : function(e)
33878     {
33879         this.fireEvent('trash', this);
33880     },
33881     
33882     download : function(e)
33883     {
33884         this.fireEvent('download', this);
33885     },
33886     
33887     loadCanvas : function(src)
33888     {   
33889         if(this.fireEvent('beforeloadcanvas', this, src) != false){
33890             
33891             this.reset();
33892             
33893             this.imageEl = document.createElement('img');
33894             
33895             var _this = this;
33896             
33897             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33898             
33899             this.imageEl.src = src;
33900         }
33901     },
33902     
33903     onLoadCanvas : function()
33904     {   
33905         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33906         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33907         
33908         this.bodyEl.un('click', this.beforeSelectFile, this);
33909         
33910         this.notifyEl.hide();
33911         this.thumbEl.show();
33912         this.footerEl.show();
33913         
33914         this.baseRotateLevel();
33915         
33916         if(this.isDocument){
33917             this.setThumbBoxSize();
33918         }
33919         
33920         this.setThumbBoxPosition();
33921         
33922         this.baseScaleLevel();
33923         
33924         this.draw();
33925         
33926         this.resize();
33927         
33928         this.canvasLoaded = true;
33929         
33930         if(this.loadMask){
33931             this.maskEl.unmask();
33932         }
33933         
33934     },
33935     
33936     setCanvasPosition : function()
33937     {   
33938         if(!this.canvasEl){
33939             return;
33940         }
33941         
33942         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33943         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33944         
33945         this.previewEl.setLeft(pw);
33946         this.previewEl.setTop(ph);
33947         
33948     },
33949     
33950     onMouseDown : function(e)
33951     {   
33952         e.stopEvent();
33953         
33954         this.dragable = true;
33955         this.pinching = false;
33956         
33957         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33958             this.dragable = false;
33959             return;
33960         }
33961         
33962         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33963         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33964         
33965     },
33966     
33967     onMouseMove : function(e)
33968     {   
33969         e.stopEvent();
33970         
33971         if(!this.canvasLoaded){
33972             return;
33973         }
33974         
33975         if (!this.dragable){
33976             return;
33977         }
33978         
33979         var minX = Math.ceil(this.thumbEl.getLeft(true));
33980         var minY = Math.ceil(this.thumbEl.getTop(true));
33981         
33982         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33983         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33984         
33985         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33986         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33987         
33988         x = x - this.mouseX;
33989         y = y - this.mouseY;
33990         
33991         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33992         var bgY = Math.ceil(y + this.previewEl.getTop(true));
33993         
33994         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33995         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33996         
33997         this.previewEl.setLeft(bgX);
33998         this.previewEl.setTop(bgY);
33999         
34000         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
34001         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
34002     },
34003     
34004     onMouseUp : function(e)
34005     {   
34006         e.stopEvent();
34007         
34008         this.dragable = false;
34009     },
34010     
34011     onMouseWheel : function(e)
34012     {   
34013         e.stopEvent();
34014         
34015         this.startScale = this.scale;
34016         
34017         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
34018         
34019         if(!this.zoomable()){
34020             this.scale = this.startScale;
34021             return;
34022         }
34023         
34024         this.draw();
34025         
34026         return;
34027     },
34028     
34029     zoomable : function()
34030     {
34031         var minScale = this.thumbEl.getWidth() / this.minWidth;
34032         
34033         if(this.minWidth < this.minHeight){
34034             minScale = this.thumbEl.getHeight() / this.minHeight;
34035         }
34036         
34037         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
34038         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
34039         
34040         if(
34041                 this.isDocument &&
34042                 (this.rotate == 0 || this.rotate == 180) && 
34043                 (
34044                     width > this.imageEl.OriginWidth || 
34045                     height > this.imageEl.OriginHeight ||
34046                     (width < this.minWidth && height < this.minHeight)
34047                 )
34048         ){
34049             return false;
34050         }
34051         
34052         if(
34053                 this.isDocument &&
34054                 (this.rotate == 90 || this.rotate == 270) && 
34055                 (
34056                     width > this.imageEl.OriginWidth || 
34057                     height > this.imageEl.OriginHeight ||
34058                     (width < this.minHeight && height < this.minWidth)
34059                 )
34060         ){
34061             return false;
34062         }
34063         
34064         if(
34065                 !this.isDocument &&
34066                 (this.rotate == 0 || this.rotate == 180) && 
34067                 (
34068                     width < this.minWidth || 
34069                     width > this.imageEl.OriginWidth || 
34070                     height < this.minHeight || 
34071                     height > this.imageEl.OriginHeight
34072                 )
34073         ){
34074             return false;
34075         }
34076         
34077         if(
34078                 !this.isDocument &&
34079                 (this.rotate == 90 || this.rotate == 270) && 
34080                 (
34081                     width < this.minHeight || 
34082                     width > this.imageEl.OriginWidth || 
34083                     height < this.minWidth || 
34084                     height > this.imageEl.OriginHeight
34085                 )
34086         ){
34087             return false;
34088         }
34089         
34090         return true;
34091         
34092     },
34093     
34094     onRotateLeft : function(e)
34095     {   
34096         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34097             
34098             var minScale = this.thumbEl.getWidth() / this.minWidth;
34099             
34100             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34101             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34102             
34103             this.startScale = this.scale;
34104             
34105             while (this.getScaleLevel() < minScale){
34106             
34107                 this.scale = this.scale + 1;
34108                 
34109                 if(!this.zoomable()){
34110                     break;
34111                 }
34112                 
34113                 if(
34114                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34115                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34116                 ){
34117                     continue;
34118                 }
34119                 
34120                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34121
34122                 this.draw();
34123                 
34124                 return;
34125             }
34126             
34127             this.scale = this.startScale;
34128             
34129             this.onRotateFail();
34130             
34131             return false;
34132         }
34133         
34134         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
34135
34136         if(this.isDocument){
34137             this.setThumbBoxSize();
34138             this.setThumbBoxPosition();
34139             this.setCanvasPosition();
34140         }
34141         
34142         this.draw();
34143         
34144         this.fireEvent('rotate', this, 'left');
34145         
34146     },
34147     
34148     onRotateRight : function(e)
34149     {
34150         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
34151             
34152             var minScale = this.thumbEl.getWidth() / this.minWidth;
34153         
34154             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
34155             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
34156             
34157             this.startScale = this.scale;
34158             
34159             while (this.getScaleLevel() < minScale){
34160             
34161                 this.scale = this.scale + 1;
34162                 
34163                 if(!this.zoomable()){
34164                     break;
34165                 }
34166                 
34167                 if(
34168                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
34169                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
34170                 ){
34171                     continue;
34172                 }
34173                 
34174                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34175
34176                 this.draw();
34177                 
34178                 return;
34179             }
34180             
34181             this.scale = this.startScale;
34182             
34183             this.onRotateFail();
34184             
34185             return false;
34186         }
34187         
34188         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
34189
34190         if(this.isDocument){
34191             this.setThumbBoxSize();
34192             this.setThumbBoxPosition();
34193             this.setCanvasPosition();
34194         }
34195         
34196         this.draw();
34197         
34198         this.fireEvent('rotate', this, 'right');
34199     },
34200     
34201     onRotateFail : function()
34202     {
34203         this.errorEl.show(true);
34204         
34205         var _this = this;
34206         
34207         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34208     },
34209     
34210     draw : function()
34211     {
34212         this.previewEl.dom.innerHTML = '';
34213         
34214         var canvasEl = document.createElement("canvas");
34215         
34216         var contextEl = canvasEl.getContext("2d");
34217         
34218         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34219         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34220         var center = this.imageEl.OriginWidth / 2;
34221         
34222         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34223             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34224             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34225             center = this.imageEl.OriginHeight / 2;
34226         }
34227         
34228         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34229         
34230         contextEl.translate(center, center);
34231         contextEl.rotate(this.rotate * Math.PI / 180);
34232
34233         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34234         
34235         this.canvasEl = document.createElement("canvas");
34236         
34237         this.contextEl = this.canvasEl.getContext("2d");
34238         
34239         switch (this.rotate) {
34240             case 0 :
34241                 
34242                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34243                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34244                 
34245                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34246                 
34247                 break;
34248             case 90 : 
34249                 
34250                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34251                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34252                 
34253                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34254                     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);
34255                     break;
34256                 }
34257                 
34258                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34259                 
34260                 break;
34261             case 180 :
34262                 
34263                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34264                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34265                 
34266                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34267                     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);
34268                     break;
34269                 }
34270                 
34271                 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);
34272                 
34273                 break;
34274             case 270 :
34275                 
34276                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34277                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34278         
34279                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34280                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34281                     break;
34282                 }
34283                 
34284                 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);
34285                 
34286                 break;
34287             default : 
34288                 break;
34289         }
34290         
34291         this.previewEl.appendChild(this.canvasEl);
34292         
34293         this.setCanvasPosition();
34294     },
34295     
34296     crop : function()
34297     {
34298         if(!this.canvasLoaded){
34299             return;
34300         }
34301         
34302         var imageCanvas = document.createElement("canvas");
34303         
34304         var imageContext = imageCanvas.getContext("2d");
34305         
34306         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34307         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34308         
34309         var center = imageCanvas.width / 2;
34310         
34311         imageContext.translate(center, center);
34312         
34313         imageContext.rotate(this.rotate * Math.PI / 180);
34314         
34315         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34316         
34317         var canvas = document.createElement("canvas");
34318         
34319         var context = canvas.getContext("2d");
34320                 
34321         canvas.width = this.minWidth;
34322         canvas.height = this.minHeight;
34323
34324         switch (this.rotate) {
34325             case 0 :
34326                 
34327                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34328                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34329                 
34330                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34331                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34332                 
34333                 var targetWidth = this.minWidth - 2 * x;
34334                 var targetHeight = this.minHeight - 2 * y;
34335                 
34336                 var scale = 1;
34337                 
34338                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34339                     scale = targetWidth / width;
34340                 }
34341                 
34342                 if(x > 0 && y == 0){
34343                     scale = targetHeight / height;
34344                 }
34345                 
34346                 if(x > 0 && y > 0){
34347                     scale = targetWidth / width;
34348                     
34349                     if(width < height){
34350                         scale = targetHeight / height;
34351                     }
34352                 }
34353                 
34354                 context.scale(scale, scale);
34355                 
34356                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34357                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34358
34359                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34360                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34361
34362                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34363                 
34364                 break;
34365             case 90 : 
34366                 
34367                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34368                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34369                 
34370                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34371                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34372                 
34373                 var targetWidth = this.minWidth - 2 * x;
34374                 var targetHeight = this.minHeight - 2 * y;
34375                 
34376                 var scale = 1;
34377                 
34378                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34379                     scale = targetWidth / width;
34380                 }
34381                 
34382                 if(x > 0 && y == 0){
34383                     scale = targetHeight / height;
34384                 }
34385                 
34386                 if(x > 0 && y > 0){
34387                     scale = targetWidth / width;
34388                     
34389                     if(width < height){
34390                         scale = targetHeight / height;
34391                     }
34392                 }
34393                 
34394                 context.scale(scale, scale);
34395                 
34396                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34397                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34398
34399                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34400                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34401                 
34402                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34403                 
34404                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34405                 
34406                 break;
34407             case 180 :
34408                 
34409                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34410                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34411                 
34412                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34413                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34414                 
34415                 var targetWidth = this.minWidth - 2 * x;
34416                 var targetHeight = this.minHeight - 2 * y;
34417                 
34418                 var scale = 1;
34419                 
34420                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34421                     scale = targetWidth / width;
34422                 }
34423                 
34424                 if(x > 0 && y == 0){
34425                     scale = targetHeight / height;
34426                 }
34427                 
34428                 if(x > 0 && y > 0){
34429                     scale = targetWidth / width;
34430                     
34431                     if(width < height){
34432                         scale = targetHeight / height;
34433                     }
34434                 }
34435                 
34436                 context.scale(scale, scale);
34437                 
34438                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34439                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34440
34441                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34442                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34443
34444                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34445                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34446                 
34447                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34448                 
34449                 break;
34450             case 270 :
34451                 
34452                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34453                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34454                 
34455                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34456                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34457                 
34458                 var targetWidth = this.minWidth - 2 * x;
34459                 var targetHeight = this.minHeight - 2 * y;
34460                 
34461                 var scale = 1;
34462                 
34463                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34464                     scale = targetWidth / width;
34465                 }
34466                 
34467                 if(x > 0 && y == 0){
34468                     scale = targetHeight / height;
34469                 }
34470                 
34471                 if(x > 0 && y > 0){
34472                     scale = targetWidth / width;
34473                     
34474                     if(width < height){
34475                         scale = targetHeight / height;
34476                     }
34477                 }
34478                 
34479                 context.scale(scale, scale);
34480                 
34481                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34482                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34483
34484                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34485                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34486                 
34487                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34488                 
34489                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34490                 
34491                 break;
34492             default : 
34493                 break;
34494         }
34495         
34496         this.cropData = canvas.toDataURL(this.cropType);
34497         
34498         if(this.fireEvent('crop', this, this.cropData) !== false){
34499             this.process(this.file, this.cropData);
34500         }
34501         
34502         return;
34503         
34504     },
34505     
34506     setThumbBoxSize : function()
34507     {
34508         var width, height;
34509         
34510         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34511             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34512             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34513             
34514             this.minWidth = width;
34515             this.minHeight = height;
34516             
34517             if(this.rotate == 90 || this.rotate == 270){
34518                 this.minWidth = height;
34519                 this.minHeight = width;
34520             }
34521         }
34522         
34523         height = 300;
34524         width = Math.ceil(this.minWidth * height / this.minHeight);
34525         
34526         if(this.minWidth > this.minHeight){
34527             width = 300;
34528             height = Math.ceil(this.minHeight * width / this.minWidth);
34529         }
34530         
34531         this.thumbEl.setStyle({
34532             width : width + 'px',
34533             height : height + 'px'
34534         });
34535
34536         return;
34537             
34538     },
34539     
34540     setThumbBoxPosition : function()
34541     {
34542         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34543         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34544         
34545         this.thumbEl.setLeft(x);
34546         this.thumbEl.setTop(y);
34547         
34548     },
34549     
34550     baseRotateLevel : function()
34551     {
34552         this.baseRotate = 1;
34553         
34554         if(
34555                 typeof(this.exif) != 'undefined' &&
34556                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34557                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34558         ){
34559             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34560         }
34561         
34562         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34563         
34564     },
34565     
34566     baseScaleLevel : function()
34567     {
34568         var width, height;
34569         
34570         if(this.isDocument){
34571             
34572             if(this.baseRotate == 6 || this.baseRotate == 8){
34573             
34574                 height = this.thumbEl.getHeight();
34575                 this.baseScale = height / this.imageEl.OriginWidth;
34576
34577                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34578                     width = this.thumbEl.getWidth();
34579                     this.baseScale = width / this.imageEl.OriginHeight;
34580                 }
34581
34582                 return;
34583             }
34584
34585             height = this.thumbEl.getHeight();
34586             this.baseScale = height / this.imageEl.OriginHeight;
34587
34588             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34589                 width = this.thumbEl.getWidth();
34590                 this.baseScale = width / this.imageEl.OriginWidth;
34591             }
34592
34593             return;
34594         }
34595         
34596         if(this.baseRotate == 6 || this.baseRotate == 8){
34597             
34598             width = this.thumbEl.getHeight();
34599             this.baseScale = width / this.imageEl.OriginHeight;
34600             
34601             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34602                 height = this.thumbEl.getWidth();
34603                 this.baseScale = height / this.imageEl.OriginHeight;
34604             }
34605             
34606             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34607                 height = this.thumbEl.getWidth();
34608                 this.baseScale = height / this.imageEl.OriginHeight;
34609                 
34610                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34611                     width = this.thumbEl.getHeight();
34612                     this.baseScale = width / this.imageEl.OriginWidth;
34613                 }
34614             }
34615             
34616             return;
34617         }
34618         
34619         width = this.thumbEl.getWidth();
34620         this.baseScale = width / this.imageEl.OriginWidth;
34621         
34622         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34623             height = this.thumbEl.getHeight();
34624             this.baseScale = height / this.imageEl.OriginHeight;
34625         }
34626         
34627         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34628             
34629             height = this.thumbEl.getHeight();
34630             this.baseScale = height / this.imageEl.OriginHeight;
34631             
34632             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34633                 width = this.thumbEl.getWidth();
34634                 this.baseScale = width / this.imageEl.OriginWidth;
34635             }
34636             
34637         }
34638         
34639         return;
34640     },
34641     
34642     getScaleLevel : function()
34643     {
34644         return this.baseScale * Math.pow(1.1, this.scale);
34645     },
34646     
34647     onTouchStart : function(e)
34648     {
34649         if(!this.canvasLoaded){
34650             this.beforeSelectFile(e);
34651             return;
34652         }
34653         
34654         var touches = e.browserEvent.touches;
34655         
34656         if(!touches){
34657             return;
34658         }
34659         
34660         if(touches.length == 1){
34661             this.onMouseDown(e);
34662             return;
34663         }
34664         
34665         if(touches.length != 2){
34666             return;
34667         }
34668         
34669         var coords = [];
34670         
34671         for(var i = 0, finger; finger = touches[i]; i++){
34672             coords.push(finger.pageX, finger.pageY);
34673         }
34674         
34675         var x = Math.pow(coords[0] - coords[2], 2);
34676         var y = Math.pow(coords[1] - coords[3], 2);
34677         
34678         this.startDistance = Math.sqrt(x + y);
34679         
34680         this.startScale = this.scale;
34681         
34682         this.pinching = true;
34683         this.dragable = false;
34684         
34685     },
34686     
34687     onTouchMove : function(e)
34688     {
34689         if(!this.pinching && !this.dragable){
34690             return;
34691         }
34692         
34693         var touches = e.browserEvent.touches;
34694         
34695         if(!touches){
34696             return;
34697         }
34698         
34699         if(this.dragable){
34700             this.onMouseMove(e);
34701             return;
34702         }
34703         
34704         var coords = [];
34705         
34706         for(var i = 0, finger; finger = touches[i]; i++){
34707             coords.push(finger.pageX, finger.pageY);
34708         }
34709         
34710         var x = Math.pow(coords[0] - coords[2], 2);
34711         var y = Math.pow(coords[1] - coords[3], 2);
34712         
34713         this.endDistance = Math.sqrt(x + y);
34714         
34715         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34716         
34717         if(!this.zoomable()){
34718             this.scale = this.startScale;
34719             return;
34720         }
34721         
34722         this.draw();
34723         
34724     },
34725     
34726     onTouchEnd : function(e)
34727     {
34728         this.pinching = false;
34729         this.dragable = false;
34730         
34731     },
34732     
34733     process : function(file, crop)
34734     {
34735         if(this.loadMask){
34736             this.maskEl.mask(this.loadingText);
34737         }
34738         
34739         this.xhr = new XMLHttpRequest();
34740         
34741         file.xhr = this.xhr;
34742
34743         this.xhr.open(this.method, this.url, true);
34744         
34745         var headers = {
34746             "Accept": "application/json",
34747             "Cache-Control": "no-cache",
34748             "X-Requested-With": "XMLHttpRequest"
34749         };
34750         
34751         for (var headerName in headers) {
34752             var headerValue = headers[headerName];
34753             if (headerValue) {
34754                 this.xhr.setRequestHeader(headerName, headerValue);
34755             }
34756         }
34757         
34758         var _this = this;
34759         
34760         this.xhr.onload = function()
34761         {
34762             _this.xhrOnLoad(_this.xhr);
34763         }
34764         
34765         this.xhr.onerror = function()
34766         {
34767             _this.xhrOnError(_this.xhr);
34768         }
34769         
34770         var formData = new FormData();
34771
34772         formData.append('returnHTML', 'NO');
34773         
34774         if(crop){
34775             formData.append('crop', crop);
34776         }
34777         
34778         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34779             formData.append(this.paramName, file, file.name);
34780         }
34781         
34782         if(typeof(file.filename) != 'undefined'){
34783             formData.append('filename', file.filename);
34784         }
34785         
34786         if(typeof(file.mimetype) != 'undefined'){
34787             formData.append('mimetype', file.mimetype);
34788         }
34789         
34790         if(this.fireEvent('arrange', this, formData) != false){
34791             this.xhr.send(formData);
34792         };
34793     },
34794     
34795     xhrOnLoad : function(xhr)
34796     {
34797         if(this.loadMask){
34798             this.maskEl.unmask();
34799         }
34800         
34801         if (xhr.readyState !== 4) {
34802             this.fireEvent('exception', this, xhr);
34803             return;
34804         }
34805
34806         var response = Roo.decode(xhr.responseText);
34807         
34808         if(!response.success){
34809             this.fireEvent('exception', this, xhr);
34810             return;
34811         }
34812         
34813         var response = Roo.decode(xhr.responseText);
34814         
34815         this.fireEvent('upload', this, response);
34816         
34817     },
34818     
34819     xhrOnError : function()
34820     {
34821         if(this.loadMask){
34822             this.maskEl.unmask();
34823         }
34824         
34825         Roo.log('xhr on error');
34826         
34827         var response = Roo.decode(xhr.responseText);
34828           
34829         Roo.log(response);
34830         
34831     },
34832     
34833     prepare : function(file)
34834     {   
34835         if(this.loadMask){
34836             this.maskEl.mask(this.loadingText);
34837         }
34838         
34839         this.file = false;
34840         this.exif = {};
34841         
34842         if(typeof(file) === 'string'){
34843             this.loadCanvas(file);
34844             return;
34845         }
34846         
34847         if(!file || !this.urlAPI){
34848             return;
34849         }
34850         
34851         this.file = file;
34852         this.cropType = file.type;
34853         
34854         var _this = this;
34855         
34856         if(this.fireEvent('prepare', this, this.file) != false){
34857             
34858             var reader = new FileReader();
34859             
34860             reader.onload = function (e) {
34861                 if (e.target.error) {
34862                     Roo.log(e.target.error);
34863                     return;
34864                 }
34865                 
34866                 var buffer = e.target.result,
34867                     dataView = new DataView(buffer),
34868                     offset = 2,
34869                     maxOffset = dataView.byteLength - 4,
34870                     markerBytes,
34871                     markerLength;
34872                 
34873                 if (dataView.getUint16(0) === 0xffd8) {
34874                     while (offset < maxOffset) {
34875                         markerBytes = dataView.getUint16(offset);
34876                         
34877                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34878                             markerLength = dataView.getUint16(offset + 2) + 2;
34879                             if (offset + markerLength > dataView.byteLength) {
34880                                 Roo.log('Invalid meta data: Invalid segment size.');
34881                                 break;
34882                             }
34883                             
34884                             if(markerBytes == 0xffe1){
34885                                 _this.parseExifData(
34886                                     dataView,
34887                                     offset,
34888                                     markerLength
34889                                 );
34890                             }
34891                             
34892                             offset += markerLength;
34893                             
34894                             continue;
34895                         }
34896                         
34897                         break;
34898                     }
34899                     
34900                 }
34901                 
34902                 var url = _this.urlAPI.createObjectURL(_this.file);
34903                 
34904                 _this.loadCanvas(url);
34905                 
34906                 return;
34907             }
34908             
34909             reader.readAsArrayBuffer(this.file);
34910             
34911         }
34912         
34913     },
34914     
34915     parseExifData : function(dataView, offset, length)
34916     {
34917         var tiffOffset = offset + 10,
34918             littleEndian,
34919             dirOffset;
34920     
34921         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34922             // No Exif data, might be XMP data instead
34923             return;
34924         }
34925         
34926         // Check for the ASCII code for "Exif" (0x45786966):
34927         if (dataView.getUint32(offset + 4) !== 0x45786966) {
34928             // No Exif data, might be XMP data instead
34929             return;
34930         }
34931         if (tiffOffset + 8 > dataView.byteLength) {
34932             Roo.log('Invalid Exif data: Invalid segment size.');
34933             return;
34934         }
34935         // Check for the two null bytes:
34936         if (dataView.getUint16(offset + 8) !== 0x0000) {
34937             Roo.log('Invalid Exif data: Missing byte alignment offset.');
34938             return;
34939         }
34940         // Check the byte alignment:
34941         switch (dataView.getUint16(tiffOffset)) {
34942         case 0x4949:
34943             littleEndian = true;
34944             break;
34945         case 0x4D4D:
34946             littleEndian = false;
34947             break;
34948         default:
34949             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34950             return;
34951         }
34952         // Check for the TIFF tag marker (0x002A):
34953         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34954             Roo.log('Invalid Exif data: Missing TIFF marker.');
34955             return;
34956         }
34957         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34958         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34959         
34960         this.parseExifTags(
34961             dataView,
34962             tiffOffset,
34963             tiffOffset + dirOffset,
34964             littleEndian
34965         );
34966     },
34967     
34968     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34969     {
34970         var tagsNumber,
34971             dirEndOffset,
34972             i;
34973         if (dirOffset + 6 > dataView.byteLength) {
34974             Roo.log('Invalid Exif data: Invalid directory offset.');
34975             return;
34976         }
34977         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34978         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34979         if (dirEndOffset + 4 > dataView.byteLength) {
34980             Roo.log('Invalid Exif data: Invalid directory size.');
34981             return;
34982         }
34983         for (i = 0; i < tagsNumber; i += 1) {
34984             this.parseExifTag(
34985                 dataView,
34986                 tiffOffset,
34987                 dirOffset + 2 + 12 * i, // tag offset
34988                 littleEndian
34989             );
34990         }
34991         // Return the offset to the next directory:
34992         return dataView.getUint32(dirEndOffset, littleEndian);
34993     },
34994     
34995     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
34996     {
34997         var tag = dataView.getUint16(offset, littleEndian);
34998         
34999         this.exif[tag] = this.getExifValue(
35000             dataView,
35001             tiffOffset,
35002             offset,
35003             dataView.getUint16(offset + 2, littleEndian), // tag type
35004             dataView.getUint32(offset + 4, littleEndian), // tag length
35005             littleEndian
35006         );
35007     },
35008     
35009     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
35010     {
35011         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
35012             tagSize,
35013             dataOffset,
35014             values,
35015             i,
35016             str,
35017             c;
35018     
35019         if (!tagType) {
35020             Roo.log('Invalid Exif data: Invalid tag type.');
35021             return;
35022         }
35023         
35024         tagSize = tagType.size * length;
35025         // Determine if the value is contained in the dataOffset bytes,
35026         // or if the value at the dataOffset is a pointer to the actual data:
35027         dataOffset = tagSize > 4 ?
35028                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
35029         if (dataOffset + tagSize > dataView.byteLength) {
35030             Roo.log('Invalid Exif data: Invalid data offset.');
35031             return;
35032         }
35033         if (length === 1) {
35034             return tagType.getValue(dataView, dataOffset, littleEndian);
35035         }
35036         values = [];
35037         for (i = 0; i < length; i += 1) {
35038             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
35039         }
35040         
35041         if (tagType.ascii) {
35042             str = '';
35043             // Concatenate the chars:
35044             for (i = 0; i < values.length; i += 1) {
35045                 c = values[i];
35046                 // Ignore the terminating NULL byte(s):
35047                 if (c === '\u0000') {
35048                     break;
35049                 }
35050                 str += c;
35051             }
35052             return str;
35053         }
35054         return values;
35055     }
35056     
35057 });
35058
35059 Roo.apply(Roo.bootstrap.UploadCropbox, {
35060     tags : {
35061         'Orientation': 0x0112
35062     },
35063     
35064     Orientation: {
35065             1: 0, //'top-left',
35066 //            2: 'top-right',
35067             3: 180, //'bottom-right',
35068 //            4: 'bottom-left',
35069 //            5: 'left-top',
35070             6: 90, //'right-top',
35071 //            7: 'right-bottom',
35072             8: 270 //'left-bottom'
35073     },
35074     
35075     exifTagTypes : {
35076         // byte, 8-bit unsigned int:
35077         1: {
35078             getValue: function (dataView, dataOffset) {
35079                 return dataView.getUint8(dataOffset);
35080             },
35081             size: 1
35082         },
35083         // ascii, 8-bit byte:
35084         2: {
35085             getValue: function (dataView, dataOffset) {
35086                 return String.fromCharCode(dataView.getUint8(dataOffset));
35087             },
35088             size: 1,
35089             ascii: true
35090         },
35091         // short, 16 bit int:
35092         3: {
35093             getValue: function (dataView, dataOffset, littleEndian) {
35094                 return dataView.getUint16(dataOffset, littleEndian);
35095             },
35096             size: 2
35097         },
35098         // long, 32 bit int:
35099         4: {
35100             getValue: function (dataView, dataOffset, littleEndian) {
35101                 return dataView.getUint32(dataOffset, littleEndian);
35102             },
35103             size: 4
35104         },
35105         // rational = two long values, first is numerator, second is denominator:
35106         5: {
35107             getValue: function (dataView, dataOffset, littleEndian) {
35108                 return dataView.getUint32(dataOffset, littleEndian) /
35109                     dataView.getUint32(dataOffset + 4, littleEndian);
35110             },
35111             size: 8
35112         },
35113         // slong, 32 bit signed int:
35114         9: {
35115             getValue: function (dataView, dataOffset, littleEndian) {
35116                 return dataView.getInt32(dataOffset, littleEndian);
35117             },
35118             size: 4
35119         },
35120         // srational, two slongs, first is numerator, second is denominator:
35121         10: {
35122             getValue: function (dataView, dataOffset, littleEndian) {
35123                 return dataView.getInt32(dataOffset, littleEndian) /
35124                     dataView.getInt32(dataOffset + 4, littleEndian);
35125             },
35126             size: 8
35127         }
35128     },
35129     
35130     footer : {
35131         STANDARD : [
35132             {
35133                 tag : 'div',
35134                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35135                 action : 'rotate-left',
35136                 cn : [
35137                     {
35138                         tag : 'button',
35139                         cls : 'btn btn-default',
35140                         html : '<i class="fa fa-undo"></i>'
35141                     }
35142                 ]
35143             },
35144             {
35145                 tag : 'div',
35146                 cls : 'btn-group roo-upload-cropbox-picture',
35147                 action : 'picture',
35148                 cn : [
35149                     {
35150                         tag : 'button',
35151                         cls : 'btn btn-default',
35152                         html : '<i class="fa fa-picture-o"></i>'
35153                     }
35154                 ]
35155             },
35156             {
35157                 tag : 'div',
35158                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35159                 action : 'rotate-right',
35160                 cn : [
35161                     {
35162                         tag : 'button',
35163                         cls : 'btn btn-default',
35164                         html : '<i class="fa fa-repeat"></i>'
35165                     }
35166                 ]
35167             }
35168         ],
35169         DOCUMENT : [
35170             {
35171                 tag : 'div',
35172                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35173                 action : 'rotate-left',
35174                 cn : [
35175                     {
35176                         tag : 'button',
35177                         cls : 'btn btn-default',
35178                         html : '<i class="fa fa-undo"></i>'
35179                     }
35180                 ]
35181             },
35182             {
35183                 tag : 'div',
35184                 cls : 'btn-group roo-upload-cropbox-download',
35185                 action : 'download',
35186                 cn : [
35187                     {
35188                         tag : 'button',
35189                         cls : 'btn btn-default',
35190                         html : '<i class="fa fa-download"></i>'
35191                     }
35192                 ]
35193             },
35194             {
35195                 tag : 'div',
35196                 cls : 'btn-group roo-upload-cropbox-crop',
35197                 action : 'crop',
35198                 cn : [
35199                     {
35200                         tag : 'button',
35201                         cls : 'btn btn-default',
35202                         html : '<i class="fa fa-crop"></i>'
35203                     }
35204                 ]
35205             },
35206             {
35207                 tag : 'div',
35208                 cls : 'btn-group roo-upload-cropbox-trash',
35209                 action : 'trash',
35210                 cn : [
35211                     {
35212                         tag : 'button',
35213                         cls : 'btn btn-default',
35214                         html : '<i class="fa fa-trash"></i>'
35215                     }
35216                 ]
35217             },
35218             {
35219                 tag : 'div',
35220                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35221                 action : 'rotate-right',
35222                 cn : [
35223                     {
35224                         tag : 'button',
35225                         cls : 'btn btn-default',
35226                         html : '<i class="fa fa-repeat"></i>'
35227                     }
35228                 ]
35229             }
35230         ],
35231         ROTATOR : [
35232             {
35233                 tag : 'div',
35234                 cls : 'btn-group roo-upload-cropbox-rotate-left',
35235                 action : 'rotate-left',
35236                 cn : [
35237                     {
35238                         tag : 'button',
35239                         cls : 'btn btn-default',
35240                         html : '<i class="fa fa-undo"></i>'
35241                     }
35242                 ]
35243             },
35244             {
35245                 tag : 'div',
35246                 cls : 'btn-group roo-upload-cropbox-rotate-right',
35247                 action : 'rotate-right',
35248                 cn : [
35249                     {
35250                         tag : 'button',
35251                         cls : 'btn btn-default',
35252                         html : '<i class="fa fa-repeat"></i>'
35253                     }
35254                 ]
35255             }
35256         ]
35257     }
35258 });
35259
35260 /*
35261 * Licence: LGPL
35262 */
35263
35264 /**
35265  * @class Roo.bootstrap.DocumentManager
35266  * @extends Roo.bootstrap.Component
35267  * Bootstrap DocumentManager class
35268  * @cfg {String} paramName default 'imageUpload'
35269  * @cfg {String} toolTipName default 'filename'
35270  * @cfg {String} method default POST
35271  * @cfg {String} url action url
35272  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35273  * @cfg {Boolean} multiple multiple upload default true
35274  * @cfg {Number} thumbSize default 300
35275  * @cfg {String} fieldLabel
35276  * @cfg {Number} labelWidth default 4
35277  * @cfg {String} labelAlign (left|top) default left
35278  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35279 * @cfg {Number} labellg set the width of label (1-12)
35280  * @cfg {Number} labelmd set the width of label (1-12)
35281  * @cfg {Number} labelsm set the width of label (1-12)
35282  * @cfg {Number} labelxs set the width of label (1-12)
35283  * 
35284  * @constructor
35285  * Create a new DocumentManager
35286  * @param {Object} config The config object
35287  */
35288
35289 Roo.bootstrap.DocumentManager = function(config){
35290     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35291     
35292     this.files = [];
35293     this.delegates = [];
35294     
35295     this.addEvents({
35296         /**
35297          * @event initial
35298          * Fire when initial the DocumentManager
35299          * @param {Roo.bootstrap.DocumentManager} this
35300          */
35301         "initial" : true,
35302         /**
35303          * @event inspect
35304          * inspect selected file
35305          * @param {Roo.bootstrap.DocumentManager} this
35306          * @param {File} file
35307          */
35308         "inspect" : true,
35309         /**
35310          * @event exception
35311          * Fire when xhr load exception
35312          * @param {Roo.bootstrap.DocumentManager} this
35313          * @param {XMLHttpRequest} xhr
35314          */
35315         "exception" : true,
35316         /**
35317          * @event afterupload
35318          * Fire when xhr load exception
35319          * @param {Roo.bootstrap.DocumentManager} this
35320          * @param {XMLHttpRequest} xhr
35321          */
35322         "afterupload" : true,
35323         /**
35324          * @event prepare
35325          * prepare the form data
35326          * @param {Roo.bootstrap.DocumentManager} this
35327          * @param {Object} formData
35328          */
35329         "prepare" : true,
35330         /**
35331          * @event remove
35332          * Fire when remove the file
35333          * @param {Roo.bootstrap.DocumentManager} this
35334          * @param {Object} file
35335          */
35336         "remove" : true,
35337         /**
35338          * @event refresh
35339          * Fire after refresh the file
35340          * @param {Roo.bootstrap.DocumentManager} this
35341          */
35342         "refresh" : true,
35343         /**
35344          * @event click
35345          * Fire after click the image
35346          * @param {Roo.bootstrap.DocumentManager} this
35347          * @param {Object} file
35348          */
35349         "click" : true,
35350         /**
35351          * @event edit
35352          * Fire when upload a image and editable set to true
35353          * @param {Roo.bootstrap.DocumentManager} this
35354          * @param {Object} file
35355          */
35356         "edit" : true,
35357         /**
35358          * @event beforeselectfile
35359          * Fire before select file
35360          * @param {Roo.bootstrap.DocumentManager} this
35361          */
35362         "beforeselectfile" : true,
35363         /**
35364          * @event process
35365          * Fire before process file
35366          * @param {Roo.bootstrap.DocumentManager} this
35367          * @param {Object} file
35368          */
35369         "process" : true,
35370         /**
35371          * @event previewrendered
35372          * Fire when preview rendered
35373          * @param {Roo.bootstrap.DocumentManager} this
35374          * @param {Object} file
35375          */
35376         "previewrendered" : true,
35377         /**
35378          */
35379         "previewResize" : true
35380         
35381     });
35382 };
35383
35384 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
35385     
35386     boxes : 0,
35387     inputName : '',
35388     thumbSize : 300,
35389     multiple : true,
35390     files : false,
35391     method : 'POST',
35392     url : '',
35393     paramName : 'imageUpload',
35394     toolTipName : 'filename',
35395     fieldLabel : '',
35396     labelWidth : 4,
35397     labelAlign : 'left',
35398     editable : true,
35399     delegates : false,
35400     xhr : false, 
35401     
35402     labellg : 0,
35403     labelmd : 0,
35404     labelsm : 0,
35405     labelxs : 0,
35406     
35407     getAutoCreate : function()
35408     {   
35409         var managerWidget = {
35410             tag : 'div',
35411             cls : 'roo-document-manager',
35412             cn : [
35413                 {
35414                     tag : 'input',
35415                     cls : 'roo-document-manager-selector',
35416                     type : 'file'
35417                 },
35418                 {
35419                     tag : 'div',
35420                     cls : 'roo-document-manager-uploader',
35421                     cn : [
35422                         {
35423                             tag : 'div',
35424                             cls : 'roo-document-manager-upload-btn',
35425                             html : '<i class="fa fa-plus"></i>'
35426                         }
35427                     ]
35428                     
35429                 }
35430             ]
35431         };
35432         
35433         var content = [
35434             {
35435                 tag : 'div',
35436                 cls : 'column col-md-12',
35437                 cn : managerWidget
35438             }
35439         ];
35440         
35441         if(this.fieldLabel.length){
35442             
35443             content = [
35444                 {
35445                     tag : 'div',
35446                     cls : 'column col-md-12',
35447                     html : this.fieldLabel
35448                 },
35449                 {
35450                     tag : 'div',
35451                     cls : 'column col-md-12',
35452                     cn : managerWidget
35453                 }
35454             ];
35455
35456             if(this.labelAlign == 'left'){
35457                 content = [
35458                     {
35459                         tag : 'div',
35460                         cls : 'column',
35461                         html : this.fieldLabel
35462                     },
35463                     {
35464                         tag : 'div',
35465                         cls : 'column',
35466                         cn : managerWidget
35467                     }
35468                 ];
35469                 
35470                 if(this.labelWidth > 12){
35471                     content[0].style = "width: " + this.labelWidth + 'px';
35472                 }
35473
35474                 if(this.labelWidth < 13 && this.labelmd == 0){
35475                     this.labelmd = this.labelWidth;
35476                 }
35477
35478                 if(this.labellg > 0){
35479                     content[0].cls += ' col-lg-' + this.labellg;
35480                     content[1].cls += ' col-lg-' + (12 - this.labellg);
35481                 }
35482
35483                 if(this.labelmd > 0){
35484                     content[0].cls += ' col-md-' + this.labelmd;
35485                     content[1].cls += ' col-md-' + (12 - this.labelmd);
35486                 }
35487
35488                 if(this.labelsm > 0){
35489                     content[0].cls += ' col-sm-' + this.labelsm;
35490                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
35491                 }
35492
35493                 if(this.labelxs > 0){
35494                     content[0].cls += ' col-xs-' + this.labelxs;
35495                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
35496                 }
35497                 
35498             }
35499         }
35500         
35501         var cfg = {
35502             tag : 'div',
35503             cls : 'row clearfix',
35504             cn : content
35505         };
35506         
35507         return cfg;
35508         
35509     },
35510     
35511     initEvents : function()
35512     {
35513         this.managerEl = this.el.select('.roo-document-manager', true).first();
35514         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35515         
35516         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35517         this.selectorEl.hide();
35518         
35519         if(this.multiple){
35520             this.selectorEl.attr('multiple', 'multiple');
35521         }
35522         
35523         this.selectorEl.on('change', this.onFileSelected, this);
35524         
35525         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35526         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35527         
35528         this.uploader.on('click', this.onUploaderClick, this);
35529         
35530         this.renderProgressDialog();
35531         
35532         var _this = this;
35533         
35534         window.addEventListener("resize", function() { _this.refresh(); } );
35535         
35536         this.fireEvent('initial', this);
35537     },
35538     
35539     renderProgressDialog : function()
35540     {
35541         var _this = this;
35542         
35543         this.progressDialog = new Roo.bootstrap.Modal({
35544             cls : 'roo-document-manager-progress-dialog',
35545             allow_close : false,
35546             animate : false,
35547             title : '',
35548             buttons : [
35549                 {
35550                     name  :'cancel',
35551                     weight : 'danger',
35552                     html : 'Cancel'
35553                 }
35554             ], 
35555             listeners : { 
35556                 btnclick : function() {
35557                     _this.uploadCancel();
35558                     this.hide();
35559                 }
35560             }
35561         });
35562          
35563         this.progressDialog.render(Roo.get(document.body));
35564          
35565         this.progress = new Roo.bootstrap.Progress({
35566             cls : 'roo-document-manager-progress',
35567             active : true,
35568             striped : true
35569         });
35570         
35571         this.progress.render(this.progressDialog.getChildContainer());
35572         
35573         this.progressBar = new Roo.bootstrap.ProgressBar({
35574             cls : 'roo-document-manager-progress-bar',
35575             aria_valuenow : 0,
35576             aria_valuemin : 0,
35577             aria_valuemax : 12,
35578             panel : 'success'
35579         });
35580         
35581         this.progressBar.render(this.progress.getChildContainer());
35582     },
35583     
35584     onUploaderClick : function(e)
35585     {
35586         e.preventDefault();
35587      
35588         if(this.fireEvent('beforeselectfile', this) != false){
35589             this.selectorEl.dom.click();
35590         }
35591         
35592     },
35593     
35594     onFileSelected : function(e)
35595     {
35596         e.preventDefault();
35597         
35598         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35599             return;
35600         }
35601         
35602         Roo.each(this.selectorEl.dom.files, function(file){
35603             if(this.fireEvent('inspect', this, file) != false){
35604                 this.files.push(file);
35605             }
35606         }, this);
35607         
35608         this.queue();
35609         
35610     },
35611     
35612     queue : function()
35613     {
35614         this.selectorEl.dom.value = '';
35615         
35616         if(!this.files || !this.files.length){
35617             return;
35618         }
35619         
35620         if(this.boxes > 0 && this.files.length > this.boxes){
35621             this.files = this.files.slice(0, this.boxes);
35622         }
35623         
35624         this.uploader.show();
35625         
35626         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35627             this.uploader.hide();
35628         }
35629         
35630         var _this = this;
35631         
35632         var files = [];
35633         
35634         var docs = [];
35635         
35636         Roo.each(this.files, function(file){
35637             
35638             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35639                 var f = this.renderPreview(file);
35640                 files.push(f);
35641                 return;
35642             }
35643             
35644             if(file.type.indexOf('image') != -1){
35645                 this.delegates.push(
35646                     (function(){
35647                         _this.process(file);
35648                     }).createDelegate(this)
35649                 );
35650         
35651                 return;
35652             }
35653             
35654             docs.push(
35655                 (function(){
35656                     _this.process(file);
35657                 }).createDelegate(this)
35658             );
35659             
35660         }, this);
35661         
35662         this.files = files;
35663         
35664         this.delegates = this.delegates.concat(docs);
35665         
35666         if(!this.delegates.length){
35667             this.refresh();
35668             return;
35669         }
35670         
35671         this.progressBar.aria_valuemax = this.delegates.length;
35672         
35673         this.arrange();
35674         
35675         return;
35676     },
35677     
35678     arrange : function()
35679     {
35680         if(!this.delegates.length){
35681             this.progressDialog.hide();
35682             this.refresh();
35683             return;
35684         }
35685         
35686         var delegate = this.delegates.shift();
35687         
35688         this.progressDialog.show();
35689         
35690         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35691         
35692         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35693         
35694         delegate();
35695     },
35696     
35697     refresh : function()
35698     {
35699         this.uploader.show();
35700         
35701         if(this.boxes > 0 && this.files.length > this.boxes - 1){
35702             this.uploader.hide();
35703         }
35704         
35705         Roo.isTouch ? this.closable(false) : this.closable(true);
35706         
35707         this.fireEvent('refresh', this);
35708     },
35709     
35710     onRemove : function(e, el, o)
35711     {
35712         e.preventDefault();
35713         
35714         this.fireEvent('remove', this, o);
35715         
35716     },
35717     
35718     remove : function(o)
35719     {
35720         var files = [];
35721         
35722         Roo.each(this.files, function(file){
35723             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35724                 files.push(file);
35725                 return;
35726             }
35727
35728             o.target.remove();
35729
35730         }, this);
35731         
35732         this.files = files;
35733         
35734         this.refresh();
35735     },
35736     
35737     clear : function()
35738     {
35739         Roo.each(this.files, function(file){
35740             if(!file.target){
35741                 return;
35742             }
35743             
35744             file.target.remove();
35745
35746         }, this);
35747         
35748         this.files = [];
35749         
35750         this.refresh();
35751     },
35752     
35753     onClick : function(e, el, o)
35754     {
35755         e.preventDefault();
35756         
35757         this.fireEvent('click', this, o);
35758         
35759     },
35760     
35761     closable : function(closable)
35762     {
35763         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35764             
35765             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35766             
35767             if(closable){
35768                 el.show();
35769                 return;
35770             }
35771             
35772             el.hide();
35773             
35774         }, this);
35775     },
35776     
35777     xhrOnLoad : function(xhr)
35778     {
35779         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35780             el.remove();
35781         }, this);
35782         
35783         if (xhr.readyState !== 4) {
35784             this.arrange();
35785             this.fireEvent('exception', this, xhr);
35786             return;
35787         }
35788
35789         var response = Roo.decode(xhr.responseText);
35790         
35791         if(!response.success){
35792             this.arrange();
35793             this.fireEvent('exception', this, xhr);
35794             return;
35795         }
35796         
35797         var file = this.renderPreview(response.data);
35798         
35799         this.files.push(file);
35800         
35801         this.arrange();
35802         
35803         this.fireEvent('afterupload', this, xhr);
35804         
35805     },
35806     
35807     xhrOnError : function(xhr)
35808     {
35809         Roo.log('xhr on error');
35810         
35811         var response = Roo.decode(xhr.responseText);
35812           
35813         Roo.log(response);
35814         
35815         this.arrange();
35816     },
35817     
35818     process : function(file)
35819     {
35820         if(this.fireEvent('process', this, file) !== false){
35821             if(this.editable && file.type.indexOf('image') != -1){
35822                 this.fireEvent('edit', this, file);
35823                 return;
35824             }
35825
35826             this.uploadStart(file, false);
35827
35828             return;
35829         }
35830         
35831     },
35832     
35833     uploadStart : function(file, crop)
35834     {
35835         this.xhr = new XMLHttpRequest();
35836         
35837         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35838             this.arrange();
35839             return;
35840         }
35841         
35842         file.xhr = this.xhr;
35843             
35844         this.managerEl.createChild({
35845             tag : 'div',
35846             cls : 'roo-document-manager-loading',
35847             cn : [
35848                 {
35849                     tag : 'div',
35850                     tooltip : file.name,
35851                     cls : 'roo-document-manager-thumb',
35852                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35853                 }
35854             ]
35855
35856         });
35857
35858         this.xhr.open(this.method, this.url, true);
35859         
35860         var headers = {
35861             "Accept": "application/json",
35862             "Cache-Control": "no-cache",
35863             "X-Requested-With": "XMLHttpRequest"
35864         };
35865         
35866         for (var headerName in headers) {
35867             var headerValue = headers[headerName];
35868             if (headerValue) {
35869                 this.xhr.setRequestHeader(headerName, headerValue);
35870             }
35871         }
35872         
35873         var _this = this;
35874         
35875         this.xhr.onload = function()
35876         {
35877             _this.xhrOnLoad(_this.xhr);
35878         }
35879         
35880         this.xhr.onerror = function()
35881         {
35882             _this.xhrOnError(_this.xhr);
35883         }
35884         
35885         var formData = new FormData();
35886
35887         formData.append('returnHTML', 'NO');
35888         
35889         if(crop){
35890             formData.append('crop', crop);
35891         }
35892         
35893         formData.append(this.paramName, file, file.name);
35894         
35895         var options = {
35896             file : file, 
35897             manually : false
35898         };
35899         
35900         if(this.fireEvent('prepare', this, formData, options) != false){
35901             
35902             if(options.manually){
35903                 return;
35904             }
35905             
35906             this.xhr.send(formData);
35907             return;
35908         };
35909         
35910         this.uploadCancel();
35911     },
35912     
35913     uploadCancel : function()
35914     {
35915         if (this.xhr) {
35916             this.xhr.abort();
35917         }
35918         
35919         this.delegates = [];
35920         
35921         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35922             el.remove();
35923         }, this);
35924         
35925         this.arrange();
35926     },
35927     
35928     renderPreview : function(file)
35929     {
35930         if(typeof(file.target) != 'undefined' && file.target){
35931             return file;
35932         }
35933         
35934         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35935         
35936         var previewEl = this.managerEl.createChild({
35937             tag : 'div',
35938             cls : 'roo-document-manager-preview',
35939             cn : [
35940                 {
35941                     tag : 'div',
35942                     tooltip : file[this.toolTipName],
35943                     cls : 'roo-document-manager-thumb',
35944                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35945                 },
35946                 {
35947                     tag : 'button',
35948                     cls : 'close',
35949                     html : '<i class="fa fa-times-circle"></i>'
35950                 }
35951             ]
35952         });
35953
35954         var close = previewEl.select('button.close', true).first();
35955
35956         close.on('click', this.onRemove, this, file);
35957
35958         file.target = previewEl;
35959
35960         var image = previewEl.select('img', true).first();
35961         
35962         var _this = this;
35963         
35964         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35965         
35966         image.on('click', this.onClick, this, file);
35967         
35968         this.fireEvent('previewrendered', this, file);
35969         
35970         return file;
35971         
35972     },
35973     
35974     onPreviewLoad : function(file, image)
35975     {
35976         if(typeof(file.target) == 'undefined' || !file.target){
35977             return;
35978         }
35979         
35980         var width = image.dom.naturalWidth || image.dom.width;
35981         var height = image.dom.naturalHeight || image.dom.height;
35982         
35983         if(!this.previewResize) {
35984             return;
35985         }
35986         
35987         if(width > height){
35988             file.target.addClass('wide');
35989             return;
35990         }
35991         
35992         file.target.addClass('tall');
35993         return;
35994         
35995     },
35996     
35997     uploadFromSource : function(file, crop)
35998     {
35999         this.xhr = new XMLHttpRequest();
36000         
36001         this.managerEl.createChild({
36002             tag : 'div',
36003             cls : 'roo-document-manager-loading',
36004             cn : [
36005                 {
36006                     tag : 'div',
36007                     tooltip : file.name,
36008                     cls : 'roo-document-manager-thumb',
36009                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
36010                 }
36011             ]
36012
36013         });
36014
36015         this.xhr.open(this.method, this.url, true);
36016         
36017         var headers = {
36018             "Accept": "application/json",
36019             "Cache-Control": "no-cache",
36020             "X-Requested-With": "XMLHttpRequest"
36021         };
36022         
36023         for (var headerName in headers) {
36024             var headerValue = headers[headerName];
36025             if (headerValue) {
36026                 this.xhr.setRequestHeader(headerName, headerValue);
36027             }
36028         }
36029         
36030         var _this = this;
36031         
36032         this.xhr.onload = function()
36033         {
36034             _this.xhrOnLoad(_this.xhr);
36035         }
36036         
36037         this.xhr.onerror = function()
36038         {
36039             _this.xhrOnError(_this.xhr);
36040         }
36041         
36042         var formData = new FormData();
36043
36044         formData.append('returnHTML', 'NO');
36045         
36046         formData.append('crop', crop);
36047         
36048         if(typeof(file.filename) != 'undefined'){
36049             formData.append('filename', file.filename);
36050         }
36051         
36052         if(typeof(file.mimetype) != 'undefined'){
36053             formData.append('mimetype', file.mimetype);
36054         }
36055         
36056         Roo.log(formData);
36057         
36058         if(this.fireEvent('prepare', this, formData) != false){
36059             this.xhr.send(formData);
36060         };
36061     }
36062 });
36063
36064 /*
36065 * Licence: LGPL
36066 */
36067
36068 /**
36069  * @class Roo.bootstrap.DocumentViewer
36070  * @extends Roo.bootstrap.Component
36071  * Bootstrap DocumentViewer class
36072  * @cfg {Boolean} showDownload (true|false) show download button (default true)
36073  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
36074  * 
36075  * @constructor
36076  * Create a new DocumentViewer
36077  * @param {Object} config The config object
36078  */
36079
36080 Roo.bootstrap.DocumentViewer = function(config){
36081     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
36082     
36083     this.addEvents({
36084         /**
36085          * @event initial
36086          * Fire after initEvent
36087          * @param {Roo.bootstrap.DocumentViewer} this
36088          */
36089         "initial" : true,
36090         /**
36091          * @event click
36092          * Fire after click
36093          * @param {Roo.bootstrap.DocumentViewer} this
36094          */
36095         "click" : true,
36096         /**
36097          * @event download
36098          * Fire after download button
36099          * @param {Roo.bootstrap.DocumentViewer} this
36100          */
36101         "download" : true,
36102         /**
36103          * @event trash
36104          * Fire after trash button
36105          * @param {Roo.bootstrap.DocumentViewer} this
36106          */
36107         "trash" : true
36108         
36109     });
36110 };
36111
36112 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
36113     
36114     showDownload : true,
36115     
36116     showTrash : true,
36117     
36118     getAutoCreate : function()
36119     {
36120         var cfg = {
36121             tag : 'div',
36122             cls : 'roo-document-viewer',
36123             cn : [
36124                 {
36125                     tag : 'div',
36126                     cls : 'roo-document-viewer-body',
36127                     cn : [
36128                         {
36129                             tag : 'div',
36130                             cls : 'roo-document-viewer-thumb',
36131                             cn : [
36132                                 {
36133                                     tag : 'img',
36134                                     cls : 'roo-document-viewer-image'
36135                                 }
36136                             ]
36137                         }
36138                     ]
36139                 },
36140                 {
36141                     tag : 'div',
36142                     cls : 'roo-document-viewer-footer',
36143                     cn : {
36144                         tag : 'div',
36145                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
36146                         cn : [
36147                             {
36148                                 tag : 'div',
36149                                 cls : 'btn-group roo-document-viewer-download',
36150                                 cn : [
36151                                     {
36152                                         tag : 'button',
36153                                         cls : 'btn btn-default',
36154                                         html : '<i class="fa fa-download"></i>'
36155                                     }
36156                                 ]
36157                             },
36158                             {
36159                                 tag : 'div',
36160                                 cls : 'btn-group roo-document-viewer-trash',
36161                                 cn : [
36162                                     {
36163                                         tag : 'button',
36164                                         cls : 'btn btn-default',
36165                                         html : '<i class="fa fa-trash"></i>'
36166                                     }
36167                                 ]
36168                             }
36169                         ]
36170                     }
36171                 }
36172             ]
36173         };
36174         
36175         return cfg;
36176     },
36177     
36178     initEvents : function()
36179     {
36180         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
36181         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36182         
36183         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
36184         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36185         
36186         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
36187         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36188         
36189         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
36190         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
36191         
36192         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
36193         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
36194         
36195         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
36196         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36197         
36198         this.bodyEl.on('click', this.onClick, this);
36199         this.downloadBtn.on('click', this.onDownload, this);
36200         this.trashBtn.on('click', this.onTrash, this);
36201         
36202         this.downloadBtn.hide();
36203         this.trashBtn.hide();
36204         
36205         if(this.showDownload){
36206             this.downloadBtn.show();
36207         }
36208         
36209         if(this.showTrash){
36210             this.trashBtn.show();
36211         }
36212         
36213         if(!this.showDownload && !this.showTrash) {
36214             this.footerEl.hide();
36215         }
36216         
36217     },
36218     
36219     initial : function()
36220     {
36221         this.fireEvent('initial', this);
36222         
36223     },
36224     
36225     onClick : function(e)
36226     {
36227         e.preventDefault();
36228         
36229         this.fireEvent('click', this);
36230     },
36231     
36232     onDownload : function(e)
36233     {
36234         e.preventDefault();
36235         
36236         this.fireEvent('download', this);
36237     },
36238     
36239     onTrash : function(e)
36240     {
36241         e.preventDefault();
36242         
36243         this.fireEvent('trash', this);
36244     }
36245     
36246 });
36247 /*
36248  * - LGPL
36249  *
36250  * FieldLabel
36251  * 
36252  */
36253
36254 /**
36255  * @class Roo.bootstrap.form.FieldLabel
36256  * @extends Roo.bootstrap.Component
36257  * Bootstrap FieldLabel class
36258  * @cfg {String} html contents of the element
36259  * @cfg {String} tag tag of the element default label
36260  * @cfg {String} cls class of the element
36261  * @cfg {String} target label target 
36262  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36263  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36264  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36265  * @cfg {String} iconTooltip default "This field is required"
36266  * @cfg {String} indicatorpos (left|right) default left
36267  * 
36268  * @constructor
36269  * Create a new FieldLabel
36270  * @param {Object} config The config object
36271  */
36272
36273 Roo.bootstrap.form.FieldLabel = function(config){
36274     Roo.bootstrap.Element.superclass.constructor.call(this, config);
36275     
36276     this.addEvents({
36277             /**
36278              * @event invalid
36279              * Fires after the field has been marked as invalid.
36280              * @param {Roo.form.FieldLabel} this
36281              * @param {String} msg The validation message
36282              */
36283             invalid : true,
36284             /**
36285              * @event valid
36286              * Fires after the field has been validated with no errors.
36287              * @param {Roo.form.FieldLabel} this
36288              */
36289             valid : true
36290         });
36291 };
36292
36293 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component,  {
36294     
36295     tag: 'label',
36296     cls: '',
36297     html: '',
36298     target: '',
36299     allowBlank : true,
36300     invalidClass : 'has-warning',
36301     validClass : 'has-success',
36302     iconTooltip : 'This field is required',
36303     indicatorpos : 'left',
36304     
36305     getAutoCreate : function(){
36306         
36307         var cls = "";
36308         if (!this.allowBlank) {
36309             cls  = "visible";
36310         }
36311         
36312         var cfg = {
36313             tag : this.tag,
36314             cls : 'roo-bootstrap-field-label ' + this.cls,
36315             for : this.target,
36316             cn : [
36317                 {
36318                     tag : 'i',
36319                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36320                     tooltip : this.iconTooltip
36321                 },
36322                 {
36323                     tag : 'span',
36324                     html : this.html
36325                 }
36326             ] 
36327         };
36328         
36329         if(this.indicatorpos == 'right'){
36330             var cfg = {
36331                 tag : this.tag,
36332                 cls : 'roo-bootstrap-field-label ' + this.cls,
36333                 for : this.target,
36334                 cn : [
36335                     {
36336                         tag : 'span',
36337                         html : this.html
36338                     },
36339                     {
36340                         tag : 'i',
36341                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36342                         tooltip : this.iconTooltip
36343                     }
36344                 ] 
36345             };
36346         }
36347         
36348         return cfg;
36349     },
36350     
36351     initEvents: function() 
36352     {
36353         Roo.bootstrap.Element.superclass.initEvents.call(this);
36354         
36355         this.indicator = this.indicatorEl();
36356         
36357         if(this.indicator){
36358             this.indicator.removeClass('visible');
36359             this.indicator.addClass('invisible');
36360         }
36361         
36362         Roo.bootstrap.form.FieldLabel.register(this);
36363     },
36364     
36365     indicatorEl : function()
36366     {
36367         var indicator = this.el.select('i.roo-required-indicator',true).first();
36368         
36369         if(!indicator){
36370             return false;
36371         }
36372         
36373         return indicator;
36374         
36375     },
36376     
36377     /**
36378      * Mark this field as valid
36379      */
36380     markValid : function()
36381     {
36382         if(this.indicator){
36383             this.indicator.removeClass('visible');
36384             this.indicator.addClass('invisible');
36385         }
36386         if (Roo.bootstrap.version == 3) {
36387             this.el.removeClass(this.invalidClass);
36388             this.el.addClass(this.validClass);
36389         } else {
36390             this.el.removeClass('is-invalid');
36391             this.el.addClass('is-valid');
36392         }
36393         
36394         
36395         this.fireEvent('valid', this);
36396     },
36397     
36398     /**
36399      * Mark this field as invalid
36400      * @param {String} msg The validation message
36401      */
36402     markInvalid : function(msg)
36403     {
36404         if(this.indicator){
36405             this.indicator.removeClass('invisible');
36406             this.indicator.addClass('visible');
36407         }
36408           if (Roo.bootstrap.version == 3) {
36409             this.el.removeClass(this.validClass);
36410             this.el.addClass(this.invalidClass);
36411         } else {
36412             this.el.removeClass('is-valid');
36413             this.el.addClass('is-invalid');
36414         }
36415         
36416         
36417         this.fireEvent('invalid', this, msg);
36418     }
36419     
36420    
36421 });
36422
36423 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36424     
36425     groups: {},
36426     
36427      /**
36428     * register a FieldLabel Group
36429     * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36430     */
36431     register : function(label)
36432     {
36433         if(this.groups.hasOwnProperty(label.target)){
36434             return;
36435         }
36436      
36437         this.groups[label.target] = label;
36438         
36439     },
36440     /**
36441     * fetch a FieldLabel Group based on the target
36442     * @param {string} target
36443     * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36444     */
36445     get: function(target) {
36446         if (typeof(this.groups[target]) == 'undefined') {
36447             return false;
36448         }
36449         
36450         return this.groups[target] ;
36451     }
36452 });
36453
36454  
36455
36456  /*
36457  * - LGPL
36458  *
36459  * page DateSplitField.
36460  * 
36461  */
36462
36463
36464 /**
36465  * @class Roo.bootstrap.form.DateSplitField
36466  * @extends Roo.bootstrap.Component
36467  * Bootstrap DateSplitField class
36468  * @cfg {string} fieldLabel - the label associated
36469  * @cfg {Number} labelWidth set the width of label (0-12)
36470  * @cfg {String} labelAlign (top|left)
36471  * @cfg {Boolean} dayAllowBlank (true|false) default false
36472  * @cfg {Boolean} monthAllowBlank (true|false) default false
36473  * @cfg {Boolean} yearAllowBlank (true|false) default false
36474  * @cfg {string} dayPlaceholder 
36475  * @cfg {string} monthPlaceholder
36476  * @cfg {string} yearPlaceholder
36477  * @cfg {string} dayFormat default 'd'
36478  * @cfg {string} monthFormat default 'm'
36479  * @cfg {string} yearFormat default 'Y'
36480  * @cfg {Number} labellg set the width of label (1-12)
36481  * @cfg {Number} labelmd set the width of label (1-12)
36482  * @cfg {Number} labelsm set the width of label (1-12)
36483  * @cfg {Number} labelxs set the width of label (1-12)
36484
36485  *     
36486  * @constructor
36487  * Create a new DateSplitField
36488  * @param {Object} config The config object
36489  */
36490
36491 Roo.bootstrap.form.DateSplitField = function(config){
36492     Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36493     
36494     this.addEvents({
36495         // raw events
36496          /**
36497          * @event years
36498          * getting the data of years
36499          * @param {Roo.bootstrap.form.DateSplitField} this
36500          * @param {Object} years
36501          */
36502         "years" : true,
36503         /**
36504          * @event days
36505          * getting the data of days
36506          * @param {Roo.bootstrap.form.DateSplitField} this
36507          * @param {Object} days
36508          */
36509         "days" : true,
36510         /**
36511          * @event invalid
36512          * Fires after the field has been marked as invalid.
36513          * @param {Roo.form.Field} this
36514          * @param {String} msg The validation message
36515          */
36516         invalid : true,
36517        /**
36518          * @event valid
36519          * Fires after the field has been validated with no errors.
36520          * @param {Roo.form.Field} this
36521          */
36522         valid : true
36523     });
36524 };
36525
36526 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component,  {
36527     
36528     fieldLabel : '',
36529     labelAlign : 'top',
36530     labelWidth : 3,
36531     dayAllowBlank : false,
36532     monthAllowBlank : false,
36533     yearAllowBlank : false,
36534     dayPlaceholder : '',
36535     monthPlaceholder : '',
36536     yearPlaceholder : '',
36537     dayFormat : 'd',
36538     monthFormat : 'm',
36539     yearFormat : 'Y',
36540     isFormField : true,
36541     labellg : 0,
36542     labelmd : 0,
36543     labelsm : 0,
36544     labelxs : 0,
36545     
36546     getAutoCreate : function()
36547     {
36548         var cfg = {
36549             tag : 'div',
36550             cls : 'row roo-date-split-field-group',
36551             cn : [
36552                 {
36553                     tag : 'input',
36554                     type : 'hidden',
36555                     cls : 'form-hidden-field roo-date-split-field-group-value',
36556                     name : this.name
36557                 }
36558             ]
36559         };
36560         
36561         var labelCls = 'col-md-12';
36562         var contentCls = 'col-md-4';
36563         
36564         if(this.fieldLabel){
36565             
36566             var label = {
36567                 tag : 'div',
36568                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36569                 cn : [
36570                     {
36571                         tag : 'label',
36572                         html : this.fieldLabel
36573                     }
36574                 ]
36575             };
36576             
36577             if(this.labelAlign == 'left'){
36578             
36579                 if(this.labelWidth > 12){
36580                     label.style = "width: " + this.labelWidth + 'px';
36581                 }
36582
36583                 if(this.labelWidth < 13 && this.labelmd == 0){
36584                     this.labelmd = this.labelWidth;
36585                 }
36586
36587                 if(this.labellg > 0){
36588                     labelCls = ' col-lg-' + this.labellg;
36589                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36590                 }
36591
36592                 if(this.labelmd > 0){
36593                     labelCls = ' col-md-' + this.labelmd;
36594                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36595                 }
36596
36597                 if(this.labelsm > 0){
36598                     labelCls = ' col-sm-' + this.labelsm;
36599                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36600                 }
36601
36602                 if(this.labelxs > 0){
36603                     labelCls = ' col-xs-' + this.labelxs;
36604                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36605                 }
36606             }
36607             
36608             label.cls += ' ' + labelCls;
36609             
36610             cfg.cn.push(label);
36611         }
36612         
36613         Roo.each(['day', 'month', 'year'], function(t){
36614             cfg.cn.push({
36615                 tag : 'div',
36616                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36617             });
36618         }, this);
36619         
36620         return cfg;
36621     },
36622     
36623     inputEl: function ()
36624     {
36625         return this.el.select('.roo-date-split-field-group-value', true).first();
36626     },
36627     
36628     onRender : function(ct, position) 
36629     {
36630         var _this = this;
36631         
36632         Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36633         
36634         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36635         
36636         this.dayField = new Roo.bootstrap.form.ComboBox({
36637             allowBlank : this.dayAllowBlank,
36638             alwaysQuery : true,
36639             displayField : 'value',
36640             editable : false,
36641             fieldLabel : '',
36642             forceSelection : true,
36643             mode : 'local',
36644             placeholder : this.dayPlaceholder,
36645             selectOnFocus : true,
36646             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36647             triggerAction : 'all',
36648             typeAhead : true,
36649             valueField : 'value',
36650             store : new Roo.data.SimpleStore({
36651                 data : (function() {    
36652                     var days = [];
36653                     _this.fireEvent('days', _this, days);
36654                     return days;
36655                 })(),
36656                 fields : [ 'value' ]
36657             }),
36658             listeners : {
36659                 select : function (_self, record, index)
36660                 {
36661                     _this.setValue(_this.getValue());
36662                 }
36663             }
36664         });
36665
36666         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36667         
36668         this.monthField = new Roo.bootstrap.form.MonthField({
36669             after : '<i class=\"fa fa-calendar\"></i>',
36670             allowBlank : this.monthAllowBlank,
36671             placeholder : this.monthPlaceholder,
36672             readOnly : true,
36673             listeners : {
36674                 render : function (_self)
36675                 {
36676                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
36677                         e.preventDefault();
36678                         _self.focus();
36679                     });
36680                 },
36681                 select : function (_self, oldvalue, newvalue)
36682                 {
36683                     _this.setValue(_this.getValue());
36684                 }
36685             }
36686         });
36687         
36688         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36689         
36690         this.yearField = new Roo.bootstrap.form.ComboBox({
36691             allowBlank : this.yearAllowBlank,
36692             alwaysQuery : true,
36693             displayField : 'value',
36694             editable : false,
36695             fieldLabel : '',
36696             forceSelection : true,
36697             mode : 'local',
36698             placeholder : this.yearPlaceholder,
36699             selectOnFocus : true,
36700             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36701             triggerAction : 'all',
36702             typeAhead : true,
36703             valueField : 'value',
36704             store : new Roo.data.SimpleStore({
36705                 data : (function() {
36706                     var years = [];
36707                     _this.fireEvent('years', _this, years);
36708                     return years;
36709                 })(),
36710                 fields : [ 'value' ]
36711             }),
36712             listeners : {
36713                 select : function (_self, record, index)
36714                 {
36715                     _this.setValue(_this.getValue());
36716                 }
36717             }
36718         });
36719
36720         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36721     },
36722     
36723     setValue : function(v, format)
36724     {
36725         this.inputEl.dom.value = v;
36726         
36727         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36728         
36729         var d = Date.parseDate(v, f);
36730         
36731         if(!d){
36732             this.validate();
36733             return;
36734         }
36735         
36736         this.setDay(d.format(this.dayFormat));
36737         this.setMonth(d.format(this.monthFormat));
36738         this.setYear(d.format(this.yearFormat));
36739         
36740         this.validate();
36741         
36742         return;
36743     },
36744     
36745     setDay : function(v)
36746     {
36747         this.dayField.setValue(v);
36748         this.inputEl.dom.value = this.getValue();
36749         this.validate();
36750         return;
36751     },
36752     
36753     setMonth : function(v)
36754     {
36755         this.monthField.setValue(v, true);
36756         this.inputEl.dom.value = this.getValue();
36757         this.validate();
36758         return;
36759     },
36760     
36761     setYear : function(v)
36762     {
36763         this.yearField.setValue(v);
36764         this.inputEl.dom.value = this.getValue();
36765         this.validate();
36766         return;
36767     },
36768     
36769     getDay : function()
36770     {
36771         return this.dayField.getValue();
36772     },
36773     
36774     getMonth : function()
36775     {
36776         return this.monthField.getValue();
36777     },
36778     
36779     getYear : function()
36780     {
36781         return this.yearField.getValue();
36782     },
36783     
36784     getValue : function()
36785     {
36786         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36787         
36788         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36789         
36790         return date;
36791     },
36792     
36793     reset : function()
36794     {
36795         this.setDay('');
36796         this.setMonth('');
36797         this.setYear('');
36798         this.inputEl.dom.value = '';
36799         this.validate();
36800         return;
36801     },
36802     
36803     validate : function()
36804     {
36805         var d = this.dayField.validate();
36806         var m = this.monthField.validate();
36807         var y = this.yearField.validate();
36808         
36809         var valid = true;
36810         
36811         if(
36812                 (!this.dayAllowBlank && !d) ||
36813                 (!this.monthAllowBlank && !m) ||
36814                 (!this.yearAllowBlank && !y)
36815         ){
36816             valid = false;
36817         }
36818         
36819         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36820             return valid;
36821         }
36822         
36823         if(valid){
36824             this.markValid();
36825             return valid;
36826         }
36827         
36828         this.markInvalid();
36829         
36830         return valid;
36831     },
36832     
36833     markValid : function()
36834     {
36835         
36836         var label = this.el.select('label', true).first();
36837         var icon = this.el.select('i.fa-star', true).first();
36838
36839         if(label && icon){
36840             icon.remove();
36841         }
36842         
36843         this.fireEvent('valid', this);
36844     },
36845     
36846      /**
36847      * Mark this field as invalid
36848      * @param {String} msg The validation message
36849      */
36850     markInvalid : function(msg)
36851     {
36852         
36853         var label = this.el.select('label', true).first();
36854         var icon = this.el.select('i.fa-star', true).first();
36855
36856         if(label && !icon){
36857             this.el.select('.roo-date-split-field-label', true).createChild({
36858                 tag : 'i',
36859                 cls : 'text-danger fa fa-lg fa-star',
36860                 tooltip : 'This field is required',
36861                 style : 'margin-right:5px;'
36862             }, label, true);
36863         }
36864         
36865         this.fireEvent('invalid', this, msg);
36866     },
36867     
36868     clearInvalid : function()
36869     {
36870         var label = this.el.select('label', true).first();
36871         var icon = this.el.select('i.fa-star', true).first();
36872
36873         if(label && icon){
36874             icon.remove();
36875         }
36876         
36877         this.fireEvent('valid', this);
36878     },
36879     
36880     getName: function()
36881     {
36882         return this.name;
36883     }
36884     
36885 });
36886
36887  
36888
36889 /**
36890  * @class Roo.bootstrap.LayoutMasonry
36891  * @extends Roo.bootstrap.Component
36892  * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36893  * Bootstrap Layout Masonry class
36894  *
36895  * This is based on 
36896  * http://masonry.desandro.com
36897  *
36898  * The idea is to render all the bricks based on vertical width...
36899  *
36900  * The original code extends 'outlayer' - we might need to use that....
36901
36902  * @constructor
36903  * Create a new Element
36904  * @param {Object} config The config object
36905  */
36906
36907 Roo.bootstrap.LayoutMasonry = function(config){
36908     
36909     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36910     
36911     this.bricks = [];
36912     
36913     Roo.bootstrap.LayoutMasonry.register(this);
36914     
36915     this.addEvents({
36916         // raw events
36917         /**
36918          * @event layout
36919          * Fire after layout the items
36920          * @param {Roo.bootstrap.LayoutMasonry} this
36921          * @param {Roo.EventObject} e
36922          */
36923         "layout" : true
36924     });
36925     
36926 };
36927
36928 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
36929     
36930     /**
36931      * @cfg {Boolean} isLayoutInstant = no animation?
36932      */   
36933     isLayoutInstant : false, // needed?
36934    
36935     /**
36936      * @cfg {Number} boxWidth  width of the columns
36937      */   
36938     boxWidth : 450,
36939     
36940       /**
36941      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
36942      */   
36943     boxHeight : 0,
36944     
36945     /**
36946      * @cfg {Number} padWidth padding below box..
36947      */   
36948     padWidth : 10, 
36949     
36950     /**
36951      * @cfg {Number} gutter gutter width..
36952      */   
36953     gutter : 10,
36954     
36955      /**
36956      * @cfg {Number} maxCols maximum number of columns
36957      */   
36958     
36959     maxCols: 0,
36960     
36961     /**
36962      * @cfg {Boolean} isAutoInitial defalut true
36963      */   
36964     isAutoInitial : true, 
36965     
36966     containerWidth: 0,
36967     
36968     /**
36969      * @cfg {Boolean} isHorizontal defalut false
36970      */   
36971     isHorizontal : false, 
36972
36973     currentSize : null,
36974     
36975     tag: 'div',
36976     
36977     cls: '',
36978     
36979     bricks: null, //CompositeElement
36980     
36981     cols : 1,
36982     
36983     _isLayoutInited : false,
36984     
36985 //    isAlternative : false, // only use for vertical layout...
36986     
36987     /**
36988      * @cfg {Number} alternativePadWidth padding below box..
36989      */   
36990     alternativePadWidth : 50,
36991     
36992     selectedBrick : [],
36993     
36994     getAutoCreate : function(){
36995         
36996         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36997         
36998         var cfg = {
36999             tag: this.tag,
37000             cls: 'blog-masonary-wrapper ' + this.cls,
37001             cn : {
37002                 cls : 'mas-boxes masonary'
37003             }
37004         };
37005         
37006         return cfg;
37007     },
37008     
37009     getChildContainer: function( )
37010     {
37011         if (this.boxesEl) {
37012             return this.boxesEl;
37013         }
37014         
37015         this.boxesEl = this.el.select('.mas-boxes').first();
37016         
37017         return this.boxesEl;
37018     },
37019     
37020     
37021     initEvents : function()
37022     {
37023         var _this = this;
37024         
37025         if(this.isAutoInitial){
37026             Roo.log('hook children rendered');
37027             this.on('childrenrendered', function() {
37028                 Roo.log('children rendered');
37029                 _this.initial();
37030             } ,this);
37031         }
37032     },
37033     
37034     initial : function()
37035     {
37036         this.selectedBrick = [];
37037         
37038         this.currentSize = this.el.getBox(true);
37039         
37040         Roo.EventManager.onWindowResize(this.resize, this); 
37041
37042         if(!this.isAutoInitial){
37043             this.layout();
37044             return;
37045         }
37046         
37047         this.layout();
37048         
37049         return;
37050         //this.layout.defer(500,this);
37051         
37052     },
37053     
37054     resize : function()
37055     {
37056         var cs = this.el.getBox(true);
37057         
37058         if (
37059                 this.currentSize.width == cs.width && 
37060                 this.currentSize.x == cs.x && 
37061                 this.currentSize.height == cs.height && 
37062                 this.currentSize.y == cs.y 
37063         ) {
37064             Roo.log("no change in with or X or Y");
37065             return;
37066         }
37067         
37068         this.currentSize = cs;
37069         
37070         this.layout();
37071         
37072     },
37073     
37074     layout : function()
37075     {   
37076         this._resetLayout();
37077         
37078         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
37079         
37080         this.layoutItems( isInstant );
37081       
37082         this._isLayoutInited = true;
37083         
37084         this.fireEvent('layout', this);
37085         
37086     },
37087     
37088     _resetLayout : function()
37089     {
37090         if(this.isHorizontal){
37091             this.horizontalMeasureColumns();
37092             return;
37093         }
37094         
37095         this.verticalMeasureColumns();
37096         
37097     },
37098     
37099     verticalMeasureColumns : function()
37100     {
37101         this.getContainerWidth();
37102         
37103 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37104 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
37105 //            return;
37106 //        }
37107         
37108         var boxWidth = this.boxWidth + this.padWidth;
37109         
37110         if(this.containerWidth < this.boxWidth){
37111             boxWidth = this.containerWidth
37112         }
37113         
37114         var containerWidth = this.containerWidth;
37115         
37116         var cols = Math.floor(containerWidth / boxWidth);
37117         
37118         this.cols = Math.max( cols, 1 );
37119         
37120         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
37121         
37122         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
37123         
37124         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
37125         
37126         this.colWidth = boxWidth + avail - this.padWidth;
37127         
37128         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
37129         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
37130     },
37131     
37132     horizontalMeasureColumns : function()
37133     {
37134         this.getContainerWidth();
37135         
37136         var boxWidth = this.boxWidth;
37137         
37138         if(this.containerWidth < boxWidth){
37139             boxWidth = this.containerWidth;
37140         }
37141         
37142         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
37143         
37144         this.el.setHeight(boxWidth);
37145         
37146     },
37147     
37148     getContainerWidth : function()
37149     {
37150         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
37151     },
37152     
37153     layoutItems : function( isInstant )
37154     {
37155         Roo.log(this.bricks);
37156         
37157         var items = Roo.apply([], this.bricks);
37158         
37159         if(this.isHorizontal){
37160             this._horizontalLayoutItems( items , isInstant );
37161             return;
37162         }
37163         
37164 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
37165 //            this._verticalAlternativeLayoutItems( items , isInstant );
37166 //            return;
37167 //        }
37168         
37169         this._verticalLayoutItems( items , isInstant );
37170         
37171     },
37172     
37173     _verticalLayoutItems : function ( items , isInstant)
37174     {
37175         if ( !items || !items.length ) {
37176             return;
37177         }
37178         
37179         var standard = [
37180             ['xs', 'xs', 'xs', 'tall'],
37181             ['xs', 'xs', 'tall'],
37182             ['xs', 'xs', 'sm'],
37183             ['xs', 'xs', 'xs'],
37184             ['xs', 'tall'],
37185             ['xs', 'sm'],
37186             ['xs', 'xs'],
37187             ['xs'],
37188             
37189             ['sm', 'xs', 'xs'],
37190             ['sm', 'xs'],
37191             ['sm'],
37192             
37193             ['tall', 'xs', 'xs', 'xs'],
37194             ['tall', 'xs', 'xs'],
37195             ['tall', 'xs'],
37196             ['tall']
37197             
37198         ];
37199         
37200         var queue = [];
37201         
37202         var boxes = [];
37203         
37204         var box = [];
37205         
37206         Roo.each(items, function(item, k){
37207             
37208             switch (item.size) {
37209                 // these layouts take up a full box,
37210                 case 'md' :
37211                 case 'md-left' :
37212                 case 'md-right' :
37213                 case 'wide' :
37214                     
37215                     if(box.length){
37216                         boxes.push(box);
37217                         box = [];
37218                     }
37219                     
37220                     boxes.push([item]);
37221                     
37222                     break;
37223                     
37224                 case 'xs' :
37225                 case 'sm' :
37226                 case 'tall' :
37227                     
37228                     box.push(item);
37229                     
37230                     break;
37231                 default :
37232                     break;
37233                     
37234             }
37235             
37236         }, this);
37237         
37238         if(box.length){
37239             boxes.push(box);
37240             box = [];
37241         }
37242         
37243         var filterPattern = function(box, length)
37244         {
37245             if(!box.length){
37246                 return;
37247             }
37248             
37249             var match = false;
37250             
37251             var pattern = box.slice(0, length);
37252             
37253             var format = [];
37254             
37255             Roo.each(pattern, function(i){
37256                 format.push(i.size);
37257             }, this);
37258             
37259             Roo.each(standard, function(s){
37260                 
37261                 if(String(s) != String(format)){
37262                     return;
37263                 }
37264                 
37265                 match = true;
37266                 return false;
37267                 
37268             }, this);
37269             
37270             if(!match && length == 1){
37271                 return;
37272             }
37273             
37274             if(!match){
37275                 filterPattern(box, length - 1);
37276                 return;
37277             }
37278                 
37279             queue.push(pattern);
37280
37281             box = box.slice(length, box.length);
37282
37283             filterPattern(box, 4);
37284
37285             return;
37286             
37287         }
37288         
37289         Roo.each(boxes, function(box, k){
37290             
37291             if(!box.length){
37292                 return;
37293             }
37294             
37295             if(box.length == 1){
37296                 queue.push(box);
37297                 return;
37298             }
37299             
37300             filterPattern(box, 4);
37301             
37302         }, this);
37303         
37304         this._processVerticalLayoutQueue( queue, isInstant );
37305         
37306     },
37307     
37308 //    _verticalAlternativeLayoutItems : function( items , isInstant )
37309 //    {
37310 //        if ( !items || !items.length ) {
37311 //            return;
37312 //        }
37313 //
37314 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
37315 //        
37316 //    },
37317     
37318     _horizontalLayoutItems : function ( items , isInstant)
37319     {
37320         if ( !items || !items.length || items.length < 3) {
37321             return;
37322         }
37323         
37324         items.reverse();
37325         
37326         var eItems = items.slice(0, 3);
37327         
37328         items = items.slice(3, items.length);
37329         
37330         var standard = [
37331             ['xs', 'xs', 'xs', 'wide'],
37332             ['xs', 'xs', 'wide'],
37333             ['xs', 'xs', 'sm'],
37334             ['xs', 'xs', 'xs'],
37335             ['xs', 'wide'],
37336             ['xs', 'sm'],
37337             ['xs', 'xs'],
37338             ['xs'],
37339             
37340             ['sm', 'xs', 'xs'],
37341             ['sm', 'xs'],
37342             ['sm'],
37343             
37344             ['wide', 'xs', 'xs', 'xs'],
37345             ['wide', 'xs', 'xs'],
37346             ['wide', 'xs'],
37347             ['wide'],
37348             
37349             ['wide-thin']
37350         ];
37351         
37352         var queue = [];
37353         
37354         var boxes = [];
37355         
37356         var box = [];
37357         
37358         Roo.each(items, function(item, k){
37359             
37360             switch (item.size) {
37361                 case 'md' :
37362                 case 'md-left' :
37363                 case 'md-right' :
37364                 case 'tall' :
37365                     
37366                     if(box.length){
37367                         boxes.push(box);
37368                         box = [];
37369                     }
37370                     
37371                     boxes.push([item]);
37372                     
37373                     break;
37374                     
37375                 case 'xs' :
37376                 case 'sm' :
37377                 case 'wide' :
37378                 case 'wide-thin' :
37379                     
37380                     box.push(item);
37381                     
37382                     break;
37383                 default :
37384                     break;
37385                     
37386             }
37387             
37388         }, this);
37389         
37390         if(box.length){
37391             boxes.push(box);
37392             box = [];
37393         }
37394         
37395         var filterPattern = function(box, length)
37396         {
37397             if(!box.length){
37398                 return;
37399             }
37400             
37401             var match = false;
37402             
37403             var pattern = box.slice(0, length);
37404             
37405             var format = [];
37406             
37407             Roo.each(pattern, function(i){
37408                 format.push(i.size);
37409             }, this);
37410             
37411             Roo.each(standard, function(s){
37412                 
37413                 if(String(s) != String(format)){
37414                     return;
37415                 }
37416                 
37417                 match = true;
37418                 return false;
37419                 
37420             }, this);
37421             
37422             if(!match && length == 1){
37423                 return;
37424             }
37425             
37426             if(!match){
37427                 filterPattern(box, length - 1);
37428                 return;
37429             }
37430                 
37431             queue.push(pattern);
37432
37433             box = box.slice(length, box.length);
37434
37435             filterPattern(box, 4);
37436
37437             return;
37438             
37439         }
37440         
37441         Roo.each(boxes, function(box, k){
37442             
37443             if(!box.length){
37444                 return;
37445             }
37446             
37447             if(box.length == 1){
37448                 queue.push(box);
37449                 return;
37450             }
37451             
37452             filterPattern(box, 4);
37453             
37454         }, this);
37455         
37456         
37457         var prune = [];
37458         
37459         var pos = this.el.getBox(true);
37460         
37461         var minX = pos.x;
37462         
37463         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37464         
37465         var hit_end = false;
37466         
37467         Roo.each(queue, function(box){
37468             
37469             if(hit_end){
37470                 
37471                 Roo.each(box, function(b){
37472                 
37473                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37474                     b.el.hide();
37475
37476                 }, this);
37477
37478                 return;
37479             }
37480             
37481             var mx = 0;
37482             
37483             Roo.each(box, function(b){
37484                 
37485                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37486                 b.el.show();
37487
37488                 mx = Math.max(mx, b.x);
37489                 
37490             }, this);
37491             
37492             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37493             
37494             if(maxX < minX){
37495                 
37496                 Roo.each(box, function(b){
37497                 
37498                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
37499                     b.el.hide();
37500                     
37501                 }, this);
37502                 
37503                 hit_end = true;
37504                 
37505                 return;
37506             }
37507             
37508             prune.push(box);
37509             
37510         }, this);
37511         
37512         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37513     },
37514     
37515     /** Sets position of item in DOM
37516     * @param {Element} item
37517     * @param {Number} x - horizontal position
37518     * @param {Number} y - vertical position
37519     * @param {Boolean} isInstant - disables transitions
37520     */
37521     _processVerticalLayoutQueue : function( queue, isInstant )
37522     {
37523         var pos = this.el.getBox(true);
37524         var x = pos.x;
37525         var y = pos.y;
37526         var maxY = [];
37527         
37528         for (var i = 0; i < this.cols; i++){
37529             maxY[i] = pos.y;
37530         }
37531         
37532         Roo.each(queue, function(box, k){
37533             
37534             var col = k % this.cols;
37535             
37536             Roo.each(box, function(b,kk){
37537                 
37538                 b.el.position('absolute');
37539                 
37540                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37541                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37542                 
37543                 if(b.size == 'md-left' || b.size == 'md-right'){
37544                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37545                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37546                 }
37547                 
37548                 b.el.setWidth(width);
37549                 b.el.setHeight(height);
37550                 // iframe?
37551                 b.el.select('iframe',true).setSize(width,height);
37552                 
37553             }, this);
37554             
37555             for (var i = 0; i < this.cols; i++){
37556                 
37557                 if(maxY[i] < maxY[col]){
37558                     col = i;
37559                     continue;
37560                 }
37561                 
37562                 col = Math.min(col, i);
37563                 
37564             }
37565             
37566             x = pos.x + col * (this.colWidth + this.padWidth);
37567             
37568             y = maxY[col];
37569             
37570             var positions = [];
37571             
37572             switch (box.length){
37573                 case 1 :
37574                     positions = this.getVerticalOneBoxColPositions(x, y, box);
37575                     break;
37576                 case 2 :
37577                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
37578                     break;
37579                 case 3 :
37580                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
37581                     break;
37582                 case 4 :
37583                     positions = this.getVerticalFourBoxColPositions(x, y, box);
37584                     break;
37585                 default :
37586                     break;
37587             }
37588             
37589             Roo.each(box, function(b,kk){
37590                 
37591                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37592                 
37593                 var sz = b.el.getSize();
37594                 
37595                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37596                 
37597             }, this);
37598             
37599         }, this);
37600         
37601         var mY = 0;
37602         
37603         for (var i = 0; i < this.cols; i++){
37604             mY = Math.max(mY, maxY[i]);
37605         }
37606         
37607         this.el.setHeight(mY - pos.y);
37608         
37609     },
37610     
37611 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37612 //    {
37613 //        var pos = this.el.getBox(true);
37614 //        var x = pos.x;
37615 //        var y = pos.y;
37616 //        var maxX = pos.right;
37617 //        
37618 //        var maxHeight = 0;
37619 //        
37620 //        Roo.each(items, function(item, k){
37621 //            
37622 //            var c = k % 2;
37623 //            
37624 //            item.el.position('absolute');
37625 //                
37626 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37627 //
37628 //            item.el.setWidth(width);
37629 //
37630 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37631 //
37632 //            item.el.setHeight(height);
37633 //            
37634 //            if(c == 0){
37635 //                item.el.setXY([x, y], isInstant ? false : true);
37636 //            } else {
37637 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
37638 //            }
37639 //            
37640 //            y = y + height + this.alternativePadWidth;
37641 //            
37642 //            maxHeight = maxHeight + height + this.alternativePadWidth;
37643 //            
37644 //        }, this);
37645 //        
37646 //        this.el.setHeight(maxHeight);
37647 //        
37648 //    },
37649     
37650     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37651     {
37652         var pos = this.el.getBox(true);
37653         
37654         var minX = pos.x;
37655         var minY = pos.y;
37656         
37657         var maxX = pos.right;
37658         
37659         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37660         
37661         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37662         
37663         Roo.each(queue, function(box, k){
37664             
37665             Roo.each(box, function(b, kk){
37666                 
37667                 b.el.position('absolute');
37668                 
37669                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37670                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37671                 
37672                 if(b.size == 'md-left' || b.size == 'md-right'){
37673                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37674                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37675                 }
37676                 
37677                 b.el.setWidth(width);
37678                 b.el.setHeight(height);
37679                 
37680             }, this);
37681             
37682             if(!box.length){
37683                 return;
37684             }
37685             
37686             var positions = [];
37687             
37688             switch (box.length){
37689                 case 1 :
37690                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37691                     break;
37692                 case 2 :
37693                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37694                     break;
37695                 case 3 :
37696                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37697                     break;
37698                 case 4 :
37699                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37700                     break;
37701                 default :
37702                     break;
37703             }
37704             
37705             Roo.each(box, function(b,kk){
37706                 
37707                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37708                 
37709                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37710                 
37711             }, this);
37712             
37713         }, this);
37714         
37715     },
37716     
37717     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37718     {
37719         Roo.each(eItems, function(b,k){
37720             
37721             b.size = (k == 0) ? 'sm' : 'xs';
37722             b.x = (k == 0) ? 2 : 1;
37723             b.y = (k == 0) ? 2 : 1;
37724             
37725             b.el.position('absolute');
37726             
37727             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37728                 
37729             b.el.setWidth(width);
37730             
37731             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37732             
37733             b.el.setHeight(height);
37734             
37735         }, this);
37736
37737         var positions = [];
37738         
37739         positions.push({
37740             x : maxX - this.unitWidth * 2 - this.gutter,
37741             y : minY
37742         });
37743         
37744         positions.push({
37745             x : maxX - this.unitWidth,
37746             y : minY + (this.unitWidth + this.gutter) * 2
37747         });
37748         
37749         positions.push({
37750             x : maxX - this.unitWidth * 3 - this.gutter * 2,
37751             y : minY
37752         });
37753         
37754         Roo.each(eItems, function(b,k){
37755             
37756             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37757
37758         }, this);
37759         
37760     },
37761     
37762     getVerticalOneBoxColPositions : function(x, y, box)
37763     {
37764         var pos = [];
37765         
37766         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37767         
37768         if(box[0].size == 'md-left'){
37769             rand = 0;
37770         }
37771         
37772         if(box[0].size == 'md-right'){
37773             rand = 1;
37774         }
37775         
37776         pos.push({
37777             x : x + (this.unitWidth + this.gutter) * rand,
37778             y : y
37779         });
37780         
37781         return pos;
37782     },
37783     
37784     getVerticalTwoBoxColPositions : function(x, y, box)
37785     {
37786         var pos = [];
37787         
37788         if(box[0].size == 'xs'){
37789             
37790             pos.push({
37791                 x : x,
37792                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37793             });
37794
37795             pos.push({
37796                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37797                 y : y
37798             });
37799             
37800             return pos;
37801             
37802         }
37803         
37804         pos.push({
37805             x : x,
37806             y : y
37807         });
37808
37809         pos.push({
37810             x : x + (this.unitWidth + this.gutter) * 2,
37811             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37812         });
37813         
37814         return pos;
37815         
37816     },
37817     
37818     getVerticalThreeBoxColPositions : function(x, y, box)
37819     {
37820         var pos = [];
37821         
37822         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37823             
37824             pos.push({
37825                 x : x,
37826                 y : y
37827             });
37828
37829             pos.push({
37830                 x : x + (this.unitWidth + this.gutter) * 1,
37831                 y : y
37832             });
37833             
37834             pos.push({
37835                 x : x + (this.unitWidth + this.gutter) * 2,
37836                 y : y
37837             });
37838             
37839             return pos;
37840             
37841         }
37842         
37843         if(box[0].size == 'xs' && box[1].size == 'xs'){
37844             
37845             pos.push({
37846                 x : x,
37847                 y : y
37848             });
37849
37850             pos.push({
37851                 x : x,
37852                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37853             });
37854             
37855             pos.push({
37856                 x : x + (this.unitWidth + this.gutter) * 1,
37857                 y : y
37858             });
37859             
37860             return pos;
37861             
37862         }
37863         
37864         pos.push({
37865             x : x,
37866             y : y
37867         });
37868
37869         pos.push({
37870             x : x + (this.unitWidth + this.gutter) * 2,
37871             y : y
37872         });
37873
37874         pos.push({
37875             x : x + (this.unitWidth + this.gutter) * 2,
37876             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37877         });
37878             
37879         return pos;
37880         
37881     },
37882     
37883     getVerticalFourBoxColPositions : function(x, y, box)
37884     {
37885         var pos = [];
37886         
37887         if(box[0].size == 'xs'){
37888             
37889             pos.push({
37890                 x : x,
37891                 y : y
37892             });
37893
37894             pos.push({
37895                 x : x,
37896                 y : y + (this.unitHeight + this.gutter) * 1
37897             });
37898             
37899             pos.push({
37900                 x : x,
37901                 y : y + (this.unitHeight + this.gutter) * 2
37902             });
37903             
37904             pos.push({
37905                 x : x + (this.unitWidth + this.gutter) * 1,
37906                 y : y
37907             });
37908             
37909             return pos;
37910             
37911         }
37912         
37913         pos.push({
37914             x : x,
37915             y : y
37916         });
37917
37918         pos.push({
37919             x : x + (this.unitWidth + this.gutter) * 2,
37920             y : y
37921         });
37922
37923         pos.push({
37924             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37925             y : y + (this.unitHeight + this.gutter) * 1
37926         });
37927
37928         pos.push({
37929             x : x + (this.unitWidth + this.gutter) * 2,
37930             y : y + (this.unitWidth + this.gutter) * 2
37931         });
37932
37933         return pos;
37934         
37935     },
37936     
37937     getHorizontalOneBoxColPositions : function(maxX, minY, box)
37938     {
37939         var pos = [];
37940         
37941         if(box[0].size == 'md-left'){
37942             pos.push({
37943                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37944                 y : minY
37945             });
37946             
37947             return pos;
37948         }
37949         
37950         if(box[0].size == 'md-right'){
37951             pos.push({
37952                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37953                 y : minY + (this.unitWidth + this.gutter) * 1
37954             });
37955             
37956             return pos;
37957         }
37958         
37959         var rand = Math.floor(Math.random() * (4 - box[0].y));
37960         
37961         pos.push({
37962             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37963             y : minY + (this.unitWidth + this.gutter) * rand
37964         });
37965         
37966         return pos;
37967         
37968     },
37969     
37970     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37971     {
37972         var pos = [];
37973         
37974         if(box[0].size == 'xs'){
37975             
37976             pos.push({
37977                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37978                 y : minY
37979             });
37980
37981             pos.push({
37982                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37983                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37984             });
37985             
37986             return pos;
37987             
37988         }
37989         
37990         pos.push({
37991             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37992             y : minY
37993         });
37994
37995         pos.push({
37996             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37997             y : minY + (this.unitWidth + this.gutter) * 2
37998         });
37999         
38000         return pos;
38001         
38002     },
38003     
38004     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
38005     {
38006         var pos = [];
38007         
38008         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
38009             
38010             pos.push({
38011                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38012                 y : minY
38013             });
38014
38015             pos.push({
38016                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38017                 y : minY + (this.unitWidth + this.gutter) * 1
38018             });
38019             
38020             pos.push({
38021                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38022                 y : minY + (this.unitWidth + this.gutter) * 2
38023             });
38024             
38025             return pos;
38026             
38027         }
38028         
38029         if(box[0].size == 'xs' && box[1].size == 'xs'){
38030             
38031             pos.push({
38032                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38033                 y : minY
38034             });
38035
38036             pos.push({
38037                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38038                 y : minY
38039             });
38040             
38041             pos.push({
38042                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38043                 y : minY + (this.unitWidth + this.gutter) * 1
38044             });
38045             
38046             return pos;
38047             
38048         }
38049         
38050         pos.push({
38051             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38052             y : minY
38053         });
38054
38055         pos.push({
38056             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
38057             y : minY + (this.unitWidth + this.gutter) * 2
38058         });
38059
38060         pos.push({
38061             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
38062             y : minY + (this.unitWidth + this.gutter) * 2
38063         });
38064             
38065         return pos;
38066         
38067     },
38068     
38069     getHorizontalFourBoxColPositions : function(maxX, minY, box)
38070     {
38071         var pos = [];
38072         
38073         if(box[0].size == 'xs'){
38074             
38075             pos.push({
38076                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
38082                 y : minY
38083             });
38084             
38085             pos.push({
38086                 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),
38087                 y : minY
38088             });
38089             
38090             pos.push({
38091                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
38092                 y : minY + (this.unitWidth + this.gutter) * 1
38093             });
38094             
38095             return pos;
38096             
38097         }
38098         
38099         pos.push({
38100             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
38101             y : minY
38102         });
38103         
38104         pos.push({
38105             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
38111             y : minY + (this.unitWidth + this.gutter) * 2
38112         });
38113         
38114         pos.push({
38115             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),
38116             y : minY + (this.unitWidth + this.gutter) * 2
38117         });
38118
38119         return pos;
38120         
38121     },
38122     
38123     /**
38124     * remove a Masonry Brick
38125     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
38126     */
38127     removeBrick : function(brick_id)
38128     {
38129         if (!brick_id) {
38130             return;
38131         }
38132         
38133         for (var i = 0; i<this.bricks.length; i++) {
38134             if (this.bricks[i].id == brick_id) {
38135                 this.bricks.splice(i,1);
38136                 this.el.dom.removeChild(Roo.get(brick_id).dom);
38137                 this.initial();
38138             }
38139         }
38140     },
38141     
38142     /**
38143     * adds a Masonry Brick
38144     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38145     */
38146     addBrick : function(cfg)
38147     {
38148         var cn = new Roo.bootstrap.MasonryBrick(cfg);
38149         //this.register(cn);
38150         cn.parentId = this.id;
38151         cn.render(this.el);
38152         return cn;
38153     },
38154     
38155     /**
38156     * register a Masonry Brick
38157     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
38158     */
38159     
38160     register : function(brick)
38161     {
38162         this.bricks.push(brick);
38163         brick.masonryId = this.id;
38164     },
38165     
38166     /**
38167     * clear all the Masonry Brick
38168     */
38169     clearAll : function()
38170     {
38171         this.bricks = [];
38172         //this.getChildContainer().dom.innerHTML = "";
38173         this.el.dom.innerHTML = '';
38174     },
38175     
38176     getSelected : function()
38177     {
38178         if (!this.selectedBrick) {
38179             return false;
38180         }
38181         
38182         return this.selectedBrick;
38183     }
38184 });
38185
38186 Roo.apply(Roo.bootstrap.LayoutMasonry, {
38187     
38188     groups: {},
38189      /**
38190     * register a Masonry Layout
38191     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
38192     */
38193     
38194     register : function(layout)
38195     {
38196         this.groups[layout.id] = layout;
38197     },
38198     /**
38199     * fetch a  Masonry Layout based on the masonry layout ID
38200     * @param {string} the masonry layout to add
38201     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38202     */
38203     
38204     get: function(layout_id) {
38205         if (typeof(this.groups[layout_id]) == 'undefined') {
38206             return false;
38207         }
38208         return this.groups[layout_id] ;
38209     }
38210     
38211     
38212     
38213 });
38214
38215  
38216
38217  /**
38218  *
38219  * This is based on 
38220  * http://masonry.desandro.com
38221  *
38222  * The idea is to render all the bricks based on vertical width...
38223  *
38224  * The original code extends 'outlayer' - we might need to use that....
38225  * 
38226  */
38227
38228
38229 /**
38230  * @class Roo.bootstrap.LayoutMasonryAuto
38231  * @extends Roo.bootstrap.Component
38232  * Bootstrap Layout Masonry class
38233  * 
38234  * @constructor
38235  * Create a new Element
38236  * @param {Object} config The config object
38237  */
38238
38239 Roo.bootstrap.LayoutMasonryAuto = function(config){
38240     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38241 };
38242
38243 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
38244     
38245       /**
38246      * @cfg {Boolean} isFitWidth  - resize the width..
38247      */   
38248     isFitWidth : false,  // options..
38249     /**
38250      * @cfg {Boolean} isOriginLeft = left align?
38251      */   
38252     isOriginLeft : true,
38253     /**
38254      * @cfg {Boolean} isOriginTop = top align?
38255      */   
38256     isOriginTop : false,
38257     /**
38258      * @cfg {Boolean} isLayoutInstant = no animation?
38259      */   
38260     isLayoutInstant : false, // needed?
38261     /**
38262      * @cfg {Boolean} isResizingContainer = not sure if this is used..
38263      */   
38264     isResizingContainer : true,
38265     /**
38266      * @cfg {Number} columnWidth  width of the columns 
38267      */   
38268     
38269     columnWidth : 0,
38270     
38271     /**
38272      * @cfg {Number} maxCols maximum number of columns
38273      */   
38274     
38275     maxCols: 0,
38276     /**
38277      * @cfg {Number} padHeight padding below box..
38278      */   
38279     
38280     padHeight : 10, 
38281     
38282     /**
38283      * @cfg {Boolean} isAutoInitial defalut true
38284      */   
38285     
38286     isAutoInitial : true, 
38287     
38288     // private?
38289     gutter : 0,
38290     
38291     containerWidth: 0,
38292     initialColumnWidth : 0,
38293     currentSize : null,
38294     
38295     colYs : null, // array.
38296     maxY : 0,
38297     padWidth: 10,
38298     
38299     
38300     tag: 'div',
38301     cls: '',
38302     bricks: null, //CompositeElement
38303     cols : 0, // array?
38304     // element : null, // wrapped now this.el
38305     _isLayoutInited : null, 
38306     
38307     
38308     getAutoCreate : function(){
38309         
38310         var cfg = {
38311             tag: this.tag,
38312             cls: 'blog-masonary-wrapper ' + this.cls,
38313             cn : {
38314                 cls : 'mas-boxes masonary'
38315             }
38316         };
38317         
38318         return cfg;
38319     },
38320     
38321     getChildContainer: function( )
38322     {
38323         if (this.boxesEl) {
38324             return this.boxesEl;
38325         }
38326         
38327         this.boxesEl = this.el.select('.mas-boxes').first();
38328         
38329         return this.boxesEl;
38330     },
38331     
38332     
38333     initEvents : function()
38334     {
38335         var _this = this;
38336         
38337         if(this.isAutoInitial){
38338             Roo.log('hook children rendered');
38339             this.on('childrenrendered', function() {
38340                 Roo.log('children rendered');
38341                 _this.initial();
38342             } ,this);
38343         }
38344         
38345     },
38346     
38347     initial : function()
38348     {
38349         this.reloadItems();
38350
38351         this.currentSize = this.el.getBox(true);
38352
38353         /// was window resize... - let's see if this works..
38354         Roo.EventManager.onWindowResize(this.resize, this); 
38355
38356         if(!this.isAutoInitial){
38357             this.layout();
38358             return;
38359         }
38360         
38361         this.layout.defer(500,this);
38362     },
38363     
38364     reloadItems: function()
38365     {
38366         this.bricks = this.el.select('.masonry-brick', true);
38367         
38368         this.bricks.each(function(b) {
38369             //Roo.log(b.getSize());
38370             if (!b.attr('originalwidth')) {
38371                 b.attr('originalwidth',  b.getSize().width);
38372             }
38373             
38374         });
38375         
38376         Roo.log(this.bricks.elements.length);
38377     },
38378     
38379     resize : function()
38380     {
38381         Roo.log('resize');
38382         var cs = this.el.getBox(true);
38383         
38384         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38385             Roo.log("no change in with or X");
38386             return;
38387         }
38388         this.currentSize = cs;
38389         this.layout();
38390     },
38391     
38392     layout : function()
38393     {
38394          Roo.log('layout');
38395         this._resetLayout();
38396         //this._manageStamps();
38397       
38398         // don't animate first layout
38399         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38400         this.layoutItems( isInstant );
38401       
38402         // flag for initalized
38403         this._isLayoutInited = true;
38404     },
38405     
38406     layoutItems : function( isInstant )
38407     {
38408         //var items = this._getItemsForLayout( this.items );
38409         // original code supports filtering layout items.. we just ignore it..
38410         
38411         this._layoutItems( this.bricks , isInstant );
38412       
38413         this._postLayout();
38414     },
38415     _layoutItems : function ( items , isInstant)
38416     {
38417        //this.fireEvent( 'layout', this, items );
38418     
38419
38420         if ( !items || !items.elements.length ) {
38421           // no items, emit event with empty array
38422             return;
38423         }
38424
38425         var queue = [];
38426         items.each(function(item) {
38427             Roo.log("layout item");
38428             Roo.log(item);
38429             // get x/y object from method
38430             var position = this._getItemLayoutPosition( item );
38431             // enqueue
38432             position.item = item;
38433             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38434             queue.push( position );
38435         }, this);
38436       
38437         this._processLayoutQueue( queue );
38438     },
38439     /** Sets position of item in DOM
38440     * @param {Element} item
38441     * @param {Number} x - horizontal position
38442     * @param {Number} y - vertical position
38443     * @param {Boolean} isInstant - disables transitions
38444     */
38445     _processLayoutQueue : function( queue )
38446     {
38447         for ( var i=0, len = queue.length; i < len; i++ ) {
38448             var obj = queue[i];
38449             obj.item.position('absolute');
38450             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38451         }
38452     },
38453       
38454     
38455     /**
38456     * Any logic you want to do after each layout,
38457     * i.e. size the container
38458     */
38459     _postLayout : function()
38460     {
38461         this.resizeContainer();
38462     },
38463     
38464     resizeContainer : function()
38465     {
38466         if ( !this.isResizingContainer ) {
38467             return;
38468         }
38469         var size = this._getContainerSize();
38470         if ( size ) {
38471             this.el.setSize(size.width,size.height);
38472             this.boxesEl.setSize(size.width,size.height);
38473         }
38474     },
38475     
38476     
38477     
38478     _resetLayout : function()
38479     {
38480         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38481         this.colWidth = this.el.getWidth();
38482         //this.gutter = this.el.getWidth(); 
38483         
38484         this.measureColumns();
38485
38486         // reset column Y
38487         var i = this.cols;
38488         this.colYs = [];
38489         while (i--) {
38490             this.colYs.push( 0 );
38491         }
38492     
38493         this.maxY = 0;
38494     },
38495
38496     measureColumns : function()
38497     {
38498         this.getContainerWidth();
38499       // if columnWidth is 0, default to outerWidth of first item
38500         if ( !this.columnWidth ) {
38501             var firstItem = this.bricks.first();
38502             Roo.log(firstItem);
38503             this.columnWidth  = this.containerWidth;
38504             if (firstItem && firstItem.attr('originalwidth') ) {
38505                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38506             }
38507             // columnWidth fall back to item of first element
38508             Roo.log("set column width?");
38509                         this.initialColumnWidth = this.columnWidth  ;
38510
38511             // if first elem has no width, default to size of container
38512             
38513         }
38514         
38515         
38516         if (this.initialColumnWidth) {
38517             this.columnWidth = this.initialColumnWidth;
38518         }
38519         
38520         
38521             
38522         // column width is fixed at the top - however if container width get's smaller we should
38523         // reduce it...
38524         
38525         // this bit calcs how man columns..
38526             
38527         var columnWidth = this.columnWidth += this.gutter;
38528       
38529         // calculate columns
38530         var containerWidth = this.containerWidth + this.gutter;
38531         
38532         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38533         // fix rounding errors, typically with gutters
38534         var excess = columnWidth - containerWidth % columnWidth;
38535         
38536         
38537         // if overshoot is less than a pixel, round up, otherwise floor it
38538         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38539         cols = Math[ mathMethod ]( cols );
38540         this.cols = Math.max( cols, 1 );
38541         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38542         
38543          // padding positioning..
38544         var totalColWidth = this.cols * this.columnWidth;
38545         var padavail = this.containerWidth - totalColWidth;
38546         // so for 2 columns - we need 3 'pads'
38547         
38548         var padNeeded = (1+this.cols) * this.padWidth;
38549         
38550         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38551         
38552         this.columnWidth += padExtra
38553         //this.padWidth = Math.floor(padavail /  ( this.cols));
38554         
38555         // adjust colum width so that padding is fixed??
38556         
38557         // we have 3 columns ... total = width * 3
38558         // we have X left over... that should be used by 
38559         
38560         //if (this.expandC) {
38561             
38562         //}
38563         
38564         
38565         
38566     },
38567     
38568     getContainerWidth : function()
38569     {
38570        /* // container is parent if fit width
38571         var container = this.isFitWidth ? this.element.parentNode : this.element;
38572         // check that this.size and size are there
38573         // IE8 triggers resize on body size change, so they might not be
38574         
38575         var size = getSize( container );  //FIXME
38576         this.containerWidth = size && size.innerWidth; //FIXME
38577         */
38578          
38579         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
38580         
38581     },
38582     
38583     _getItemLayoutPosition : function( item )  // what is item?
38584     {
38585         // we resize the item to our columnWidth..
38586       
38587         item.setWidth(this.columnWidth);
38588         item.autoBoxAdjust  = false;
38589         
38590         var sz = item.getSize();
38591  
38592         // how many columns does this brick span
38593         var remainder = this.containerWidth % this.columnWidth;
38594         
38595         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38596         // round if off by 1 pixel, otherwise use ceil
38597         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
38598         colSpan = Math.min( colSpan, this.cols );
38599         
38600         // normally this should be '1' as we dont' currently allow multi width columns..
38601         
38602         var colGroup = this._getColGroup( colSpan );
38603         // get the minimum Y value from the columns
38604         var minimumY = Math.min.apply( Math, colGroup );
38605         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38606         
38607         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
38608          
38609         // position the brick
38610         var position = {
38611             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38612             y: this.currentSize.y + minimumY + this.padHeight
38613         };
38614         
38615         Roo.log(position);
38616         // apply setHeight to necessary columns
38617         var setHeight = minimumY + sz.height + this.padHeight;
38618         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
38619         
38620         var setSpan = this.cols + 1 - colGroup.length;
38621         for ( var i = 0; i < setSpan; i++ ) {
38622           this.colYs[ shortColIndex + i ] = setHeight ;
38623         }
38624       
38625         return position;
38626     },
38627     
38628     /**
38629      * @param {Number} colSpan - number of columns the element spans
38630      * @returns {Array} colGroup
38631      */
38632     _getColGroup : function( colSpan )
38633     {
38634         if ( colSpan < 2 ) {
38635           // if brick spans only one column, use all the column Ys
38636           return this.colYs;
38637         }
38638       
38639         var colGroup = [];
38640         // how many different places could this brick fit horizontally
38641         var groupCount = this.cols + 1 - colSpan;
38642         // for each group potential horizontal position
38643         for ( var i = 0; i < groupCount; i++ ) {
38644           // make an array of colY values for that one group
38645           var groupColYs = this.colYs.slice( i, i + colSpan );
38646           // and get the max value of the array
38647           colGroup[i] = Math.max.apply( Math, groupColYs );
38648         }
38649         return colGroup;
38650     },
38651     /*
38652     _manageStamp : function( stamp )
38653     {
38654         var stampSize =  stamp.getSize();
38655         var offset = stamp.getBox();
38656         // get the columns that this stamp affects
38657         var firstX = this.isOriginLeft ? offset.x : offset.right;
38658         var lastX = firstX + stampSize.width;
38659         var firstCol = Math.floor( firstX / this.columnWidth );
38660         firstCol = Math.max( 0, firstCol );
38661         
38662         var lastCol = Math.floor( lastX / this.columnWidth );
38663         // lastCol should not go over if multiple of columnWidth #425
38664         lastCol -= lastX % this.columnWidth ? 0 : 1;
38665         lastCol = Math.min( this.cols - 1, lastCol );
38666         
38667         // set colYs to bottom of the stamp
38668         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38669             stampSize.height;
38670             
38671         for ( var i = firstCol; i <= lastCol; i++ ) {
38672           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38673         }
38674     },
38675     */
38676     
38677     _getContainerSize : function()
38678     {
38679         this.maxY = Math.max.apply( Math, this.colYs );
38680         var size = {
38681             height: this.maxY
38682         };
38683       
38684         if ( this.isFitWidth ) {
38685             size.width = this._getContainerFitWidth();
38686         }
38687       
38688         return size;
38689     },
38690     
38691     _getContainerFitWidth : function()
38692     {
38693         var unusedCols = 0;
38694         // count unused columns
38695         var i = this.cols;
38696         while ( --i ) {
38697           if ( this.colYs[i] !== 0 ) {
38698             break;
38699           }
38700           unusedCols++;
38701         }
38702         // fit container to columns that have been used
38703         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38704     },
38705     
38706     needsResizeLayout : function()
38707     {
38708         var previousWidth = this.containerWidth;
38709         this.getContainerWidth();
38710         return previousWidth !== this.containerWidth;
38711     }
38712  
38713 });
38714
38715  
38716
38717  /*
38718  * - LGPL
38719  *
38720  * element
38721  * 
38722  */
38723
38724 /**
38725  * @class Roo.bootstrap.MasonryBrick
38726  * @extends Roo.bootstrap.Component
38727  * Bootstrap MasonryBrick class
38728  * 
38729  * @constructor
38730  * Create a new MasonryBrick
38731  * @param {Object} config The config object
38732  */
38733
38734 Roo.bootstrap.MasonryBrick = function(config){
38735     
38736     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38737     
38738     Roo.bootstrap.MasonryBrick.register(this);
38739     
38740     this.addEvents({
38741         // raw events
38742         /**
38743          * @event click
38744          * When a MasonryBrick is clcik
38745          * @param {Roo.bootstrap.MasonryBrick} this
38746          * @param {Roo.EventObject} e
38747          */
38748         "click" : true
38749     });
38750 };
38751
38752 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
38753     
38754     /**
38755      * @cfg {String} title
38756      */   
38757     title : '',
38758     /**
38759      * @cfg {String} html
38760      */   
38761     html : '',
38762     /**
38763      * @cfg {String} bgimage
38764      */   
38765     bgimage : '',
38766     /**
38767      * @cfg {String} videourl
38768      */   
38769     videourl : '',
38770     /**
38771      * @cfg {String} cls
38772      */   
38773     cls : '',
38774     /**
38775      * @cfg {String} href
38776      */   
38777     href : '',
38778     /**
38779      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38780      */   
38781     size : 'xs',
38782     
38783     /**
38784      * @cfg {String} placetitle (center|bottom)
38785      */   
38786     placetitle : '',
38787     
38788     /**
38789      * @cfg {Boolean} isFitContainer defalut true
38790      */   
38791     isFitContainer : true, 
38792     
38793     /**
38794      * @cfg {Boolean} preventDefault defalut false
38795      */   
38796     preventDefault : false, 
38797     
38798     /**
38799      * @cfg {Boolean} inverse defalut false
38800      */   
38801     maskInverse : false, 
38802     
38803     getAutoCreate : function()
38804     {
38805         if(!this.isFitContainer){
38806             return this.getSplitAutoCreate();
38807         }
38808         
38809         var cls = 'masonry-brick masonry-brick-full';
38810         
38811         if(this.href.length){
38812             cls += ' masonry-brick-link';
38813         }
38814         
38815         if(this.bgimage.length){
38816             cls += ' masonry-brick-image';
38817         }
38818         
38819         if(this.maskInverse){
38820             cls += ' mask-inverse';
38821         }
38822         
38823         if(!this.html.length && !this.maskInverse && !this.videourl.length){
38824             cls += ' enable-mask';
38825         }
38826         
38827         if(this.size){
38828             cls += ' masonry-' + this.size + '-brick';
38829         }
38830         
38831         if(this.placetitle.length){
38832             
38833             switch (this.placetitle) {
38834                 case 'center' :
38835                     cls += ' masonry-center-title';
38836                     break;
38837                 case 'bottom' :
38838                     cls += ' masonry-bottom-title';
38839                     break;
38840                 default:
38841                     break;
38842             }
38843             
38844         } else {
38845             if(!this.html.length && !this.bgimage.length){
38846                 cls += ' masonry-center-title';
38847             }
38848
38849             if(!this.html.length && this.bgimage.length){
38850                 cls += ' masonry-bottom-title';
38851             }
38852         }
38853         
38854         if(this.cls){
38855             cls += ' ' + this.cls;
38856         }
38857         
38858         var cfg = {
38859             tag: (this.href.length) ? 'a' : 'div',
38860             cls: cls,
38861             cn: [
38862                 {
38863                     tag: 'div',
38864                     cls: 'masonry-brick-mask'
38865                 },
38866                 {
38867                     tag: 'div',
38868                     cls: 'masonry-brick-paragraph',
38869                     cn: []
38870                 }
38871             ]
38872         };
38873         
38874         if(this.href.length){
38875             cfg.href = this.href;
38876         }
38877         
38878         var cn = cfg.cn[1].cn;
38879         
38880         if(this.title.length){
38881             cn.push({
38882                 tag: 'h4',
38883                 cls: 'masonry-brick-title',
38884                 html: this.title
38885             });
38886         }
38887         
38888         if(this.html.length){
38889             cn.push({
38890                 tag: 'p',
38891                 cls: 'masonry-brick-text',
38892                 html: this.html
38893             });
38894         }
38895         
38896         if (!this.title.length && !this.html.length) {
38897             cfg.cn[1].cls += ' hide';
38898         }
38899         
38900         if(this.bgimage.length){
38901             cfg.cn.push({
38902                 tag: 'img',
38903                 cls: 'masonry-brick-image-view',
38904                 src: this.bgimage
38905             });
38906         }
38907         
38908         if(this.videourl.length){
38909             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38910             // youtube support only?
38911             cfg.cn.push({
38912                 tag: 'iframe',
38913                 cls: 'masonry-brick-image-view',
38914                 src: vurl,
38915                 frameborder : 0,
38916                 allowfullscreen : true
38917             });
38918         }
38919         
38920         return cfg;
38921         
38922     },
38923     
38924     getSplitAutoCreate : function()
38925     {
38926         var cls = 'masonry-brick masonry-brick-split';
38927         
38928         if(this.href.length){
38929             cls += ' masonry-brick-link';
38930         }
38931         
38932         if(this.bgimage.length){
38933             cls += ' masonry-brick-image';
38934         }
38935         
38936         if(this.size){
38937             cls += ' masonry-' + this.size + '-brick';
38938         }
38939         
38940         switch (this.placetitle) {
38941             case 'center' :
38942                 cls += ' masonry-center-title';
38943                 break;
38944             case 'bottom' :
38945                 cls += ' masonry-bottom-title';
38946                 break;
38947             default:
38948                 if(!this.bgimage.length){
38949                     cls += ' masonry-center-title';
38950                 }
38951
38952                 if(this.bgimage.length){
38953                     cls += ' masonry-bottom-title';
38954                 }
38955                 break;
38956         }
38957         
38958         if(this.cls){
38959             cls += ' ' + this.cls;
38960         }
38961         
38962         var cfg = {
38963             tag: (this.href.length) ? 'a' : 'div',
38964             cls: cls,
38965             cn: [
38966                 {
38967                     tag: 'div',
38968                     cls: 'masonry-brick-split-head',
38969                     cn: [
38970                         {
38971                             tag: 'div',
38972                             cls: 'masonry-brick-paragraph',
38973                             cn: []
38974                         }
38975                     ]
38976                 },
38977                 {
38978                     tag: 'div',
38979                     cls: 'masonry-brick-split-body',
38980                     cn: []
38981                 }
38982             ]
38983         };
38984         
38985         if(this.href.length){
38986             cfg.href = this.href;
38987         }
38988         
38989         if(this.title.length){
38990             cfg.cn[0].cn[0].cn.push({
38991                 tag: 'h4',
38992                 cls: 'masonry-brick-title',
38993                 html: this.title
38994             });
38995         }
38996         
38997         if(this.html.length){
38998             cfg.cn[1].cn.push({
38999                 tag: 'p',
39000                 cls: 'masonry-brick-text',
39001                 html: this.html
39002             });
39003         }
39004
39005         if(this.bgimage.length){
39006             cfg.cn[0].cn.push({
39007                 tag: 'img',
39008                 cls: 'masonry-brick-image-view',
39009                 src: this.bgimage
39010             });
39011         }
39012         
39013         if(this.videourl.length){
39014             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
39015             // youtube support only?
39016             cfg.cn[0].cn.cn.push({
39017                 tag: 'iframe',
39018                 cls: 'masonry-brick-image-view',
39019                 src: vurl,
39020                 frameborder : 0,
39021                 allowfullscreen : true
39022             });
39023         }
39024         
39025         return cfg;
39026     },
39027     
39028     initEvents: function() 
39029     {
39030         switch (this.size) {
39031             case 'xs' :
39032                 this.x = 1;
39033                 this.y = 1;
39034                 break;
39035             case 'sm' :
39036                 this.x = 2;
39037                 this.y = 2;
39038                 break;
39039             case 'md' :
39040             case 'md-left' :
39041             case 'md-right' :
39042                 this.x = 3;
39043                 this.y = 3;
39044                 break;
39045             case 'tall' :
39046                 this.x = 2;
39047                 this.y = 3;
39048                 break;
39049             case 'wide' :
39050                 this.x = 3;
39051                 this.y = 2;
39052                 break;
39053             case 'wide-thin' :
39054                 this.x = 3;
39055                 this.y = 1;
39056                 break;
39057                         
39058             default :
39059                 break;
39060         }
39061         
39062         if(Roo.isTouch){
39063             this.el.on('touchstart', this.onTouchStart, this);
39064             this.el.on('touchmove', this.onTouchMove, this);
39065             this.el.on('touchend', this.onTouchEnd, this);
39066             this.el.on('contextmenu', this.onContextMenu, this);
39067         } else {
39068             this.el.on('mouseenter'  ,this.enter, this);
39069             this.el.on('mouseleave', this.leave, this);
39070             this.el.on('click', this.onClick, this);
39071         }
39072         
39073         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
39074             this.parent().bricks.push(this);   
39075         }
39076         
39077     },
39078     
39079     onClick: function(e, el)
39080     {
39081         var time = this.endTimer - this.startTimer;
39082         // Roo.log(e.preventDefault());
39083         if(Roo.isTouch){
39084             if(time > 1000){
39085                 e.preventDefault();
39086                 return;
39087             }
39088         }
39089         
39090         if(!this.preventDefault){
39091             return;
39092         }
39093         
39094         e.preventDefault();
39095         
39096         if (this.activeClass != '') {
39097             this.selectBrick();
39098         }
39099         
39100         this.fireEvent('click', this, e);
39101     },
39102     
39103     enter: function(e, el)
39104     {
39105         e.preventDefault();
39106         
39107         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
39108             return;
39109         }
39110         
39111         if(this.bgimage.length && this.html.length){
39112             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39113         }
39114     },
39115     
39116     leave: function(e, el)
39117     {
39118         e.preventDefault();
39119         
39120         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
39121             return;
39122         }
39123         
39124         if(this.bgimage.length && this.html.length){
39125             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39126         }
39127     },
39128     
39129     onTouchStart: function(e, el)
39130     {
39131 //        e.preventDefault();
39132         
39133         this.touchmoved = false;
39134         
39135         if(!this.isFitContainer){
39136             return;
39137         }
39138         
39139         if(!this.bgimage.length || !this.html.length){
39140             return;
39141         }
39142         
39143         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
39144         
39145         this.timer = new Date().getTime();
39146         
39147     },
39148     
39149     onTouchMove: function(e, el)
39150     {
39151         this.touchmoved = true;
39152     },
39153     
39154     onContextMenu : function(e,el)
39155     {
39156         e.preventDefault();
39157         e.stopPropagation();
39158         return false;
39159     },
39160     
39161     onTouchEnd: function(e, el)
39162     {
39163 //        e.preventDefault();
39164         
39165         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
39166         
39167             this.leave(e,el);
39168             
39169             return;
39170         }
39171         
39172         if(!this.bgimage.length || !this.html.length){
39173             
39174             if(this.href.length){
39175                 window.location.href = this.href;
39176             }
39177             
39178             return;
39179         }
39180         
39181         if(!this.isFitContainer){
39182             return;
39183         }
39184         
39185         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
39186         
39187         window.location.href = this.href;
39188     },
39189     
39190     //selection on single brick only
39191     selectBrick : function() {
39192         
39193         if (!this.parentId) {
39194             return;
39195         }
39196         
39197         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39198         var index = m.selectedBrick.indexOf(this.id);
39199         
39200         if ( index > -1) {
39201             m.selectedBrick.splice(index,1);
39202             this.el.removeClass(this.activeClass);
39203             return;
39204         }
39205         
39206         for(var i = 0; i < m.selectedBrick.length; i++) {
39207             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39208             b.el.removeClass(b.activeClass);
39209         }
39210         
39211         m.selectedBrick = [];
39212         
39213         m.selectedBrick.push(this.id);
39214         this.el.addClass(this.activeClass);
39215         return;
39216     },
39217     
39218     isSelected : function(){
39219         return this.el.hasClass(this.activeClass);
39220         
39221     }
39222 });
39223
39224 Roo.apply(Roo.bootstrap.MasonryBrick, {
39225     
39226     //groups: {},
39227     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39228      /**
39229     * register a Masonry Brick
39230     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39231     */
39232     
39233     register : function(brick)
39234     {
39235         //this.groups[brick.id] = brick;
39236         this.groups.add(brick.id, brick);
39237     },
39238     /**
39239     * fetch a  masonry brick based on the masonry brick ID
39240     * @param {string} the masonry brick to add
39241     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39242     */
39243     
39244     get: function(brick_id) 
39245     {
39246         // if (typeof(this.groups[brick_id]) == 'undefined') {
39247         //     return false;
39248         // }
39249         // return this.groups[brick_id] ;
39250         
39251         if(this.groups.key(brick_id)) {
39252             return this.groups.key(brick_id);
39253         }
39254         
39255         return false;
39256     }
39257     
39258     
39259     
39260 });
39261
39262  /*
39263  * - LGPL
39264  *
39265  * element
39266  * 
39267  */
39268
39269 /**
39270  * @class Roo.bootstrap.Brick
39271  * @extends Roo.bootstrap.Component
39272  * Bootstrap Brick class
39273  * 
39274  * @constructor
39275  * Create a new Brick
39276  * @param {Object} config The config object
39277  */
39278
39279 Roo.bootstrap.Brick = function(config){
39280     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39281     
39282     this.addEvents({
39283         // raw events
39284         /**
39285          * @event click
39286          * When a Brick is click
39287          * @param {Roo.bootstrap.Brick} this
39288          * @param {Roo.EventObject} e
39289          */
39290         "click" : true
39291     });
39292 };
39293
39294 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
39295     
39296     /**
39297      * @cfg {String} title
39298      */   
39299     title : '',
39300     /**
39301      * @cfg {String} html
39302      */   
39303     html : '',
39304     /**
39305      * @cfg {String} bgimage
39306      */   
39307     bgimage : '',
39308     /**
39309      * @cfg {String} cls
39310      */   
39311     cls : '',
39312     /**
39313      * @cfg {String} href
39314      */   
39315     href : '',
39316     /**
39317      * @cfg {String} video
39318      */   
39319     video : '',
39320     /**
39321      * @cfg {Boolean} square
39322      */   
39323     square : true,
39324     
39325     getAutoCreate : function()
39326     {
39327         var cls = 'roo-brick';
39328         
39329         if(this.href.length){
39330             cls += ' roo-brick-link';
39331         }
39332         
39333         if(this.bgimage.length){
39334             cls += ' roo-brick-image';
39335         }
39336         
39337         if(!this.html.length && !this.bgimage.length){
39338             cls += ' roo-brick-center-title';
39339         }
39340         
39341         if(!this.html.length && this.bgimage.length){
39342             cls += ' roo-brick-bottom-title';
39343         }
39344         
39345         if(this.cls){
39346             cls += ' ' + this.cls;
39347         }
39348         
39349         var cfg = {
39350             tag: (this.href.length) ? 'a' : 'div',
39351             cls: cls,
39352             cn: [
39353                 {
39354                     tag: 'div',
39355                     cls: 'roo-brick-paragraph',
39356                     cn: []
39357                 }
39358             ]
39359         };
39360         
39361         if(this.href.length){
39362             cfg.href = this.href;
39363         }
39364         
39365         var cn = cfg.cn[0].cn;
39366         
39367         if(this.title.length){
39368             cn.push({
39369                 tag: 'h4',
39370                 cls: 'roo-brick-title',
39371                 html: this.title
39372             });
39373         }
39374         
39375         if(this.html.length){
39376             cn.push({
39377                 tag: 'p',
39378                 cls: 'roo-brick-text',
39379                 html: this.html
39380             });
39381         } else {
39382             cn.cls += ' hide';
39383         }
39384         
39385         if(this.bgimage.length){
39386             cfg.cn.push({
39387                 tag: 'img',
39388                 cls: 'roo-brick-image-view',
39389                 src: this.bgimage
39390             });
39391         }
39392         
39393         return cfg;
39394     },
39395     
39396     initEvents: function() 
39397     {
39398         if(this.title.length || this.html.length){
39399             this.el.on('mouseenter'  ,this.enter, this);
39400             this.el.on('mouseleave', this.leave, this);
39401         }
39402         
39403         Roo.EventManager.onWindowResize(this.resize, this); 
39404         
39405         if(this.bgimage.length){
39406             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39407             this.imageEl.on('load', this.onImageLoad, this);
39408             return;
39409         }
39410         
39411         this.resize();
39412     },
39413     
39414     onImageLoad : function()
39415     {
39416         this.resize();
39417     },
39418     
39419     resize : function()
39420     {
39421         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39422         
39423         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39424         
39425         if(this.bgimage.length){
39426             var image = this.el.select('.roo-brick-image-view', true).first();
39427             
39428             image.setWidth(paragraph.getWidth());
39429             
39430             if(this.square){
39431                 image.setHeight(paragraph.getWidth());
39432             }
39433             
39434             this.el.setHeight(image.getHeight());
39435             paragraph.setHeight(image.getHeight());
39436             
39437         }
39438         
39439     },
39440     
39441     enter: function(e, el)
39442     {
39443         e.preventDefault();
39444         
39445         if(this.bgimage.length){
39446             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39447             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39448         }
39449     },
39450     
39451     leave: function(e, el)
39452     {
39453         e.preventDefault();
39454         
39455         if(this.bgimage.length){
39456             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39457             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39458         }
39459     }
39460     
39461 });
39462
39463  
39464
39465  /*
39466  * - LGPL
39467  *
39468  * Number field 
39469  */
39470
39471 /**
39472  * @class Roo.bootstrap.form.NumberField
39473  * @extends Roo.bootstrap.form.Input
39474  * Bootstrap NumberField class
39475  * 
39476  * 
39477  * 
39478  * 
39479  * @constructor
39480  * Create a new NumberField
39481  * @param {Object} config The config object
39482  */
39483
39484 Roo.bootstrap.form.NumberField = function(config){
39485     Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39486 };
39487
39488 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39489     
39490     /**
39491      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39492      */
39493     allowDecimals : true,
39494     /**
39495      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39496      */
39497     decimalSeparator : ".",
39498     /**
39499      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39500      */
39501     decimalPrecision : 2,
39502     /**
39503      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39504      */
39505     allowNegative : true,
39506     
39507     /**
39508      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39509      */
39510     allowZero: true,
39511     /**
39512      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39513      */
39514     minValue : Number.NEGATIVE_INFINITY,
39515     /**
39516      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39517      */
39518     maxValue : Number.MAX_VALUE,
39519     /**
39520      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39521      */
39522     minText : "The minimum value for this field is {0}",
39523     /**
39524      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39525      */
39526     maxText : "The maximum value for this field is {0}",
39527     /**
39528      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39529      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39530      */
39531     nanText : "{0} is not a valid number",
39532     /**
39533      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39534      */
39535     thousandsDelimiter : false,
39536     /**
39537      * @cfg {String} valueAlign alignment of value
39538      */
39539     valueAlign : "left",
39540
39541     getAutoCreate : function()
39542     {
39543         var hiddenInput = {
39544             tag: 'input',
39545             type: 'hidden',
39546             id: Roo.id(),
39547             cls: 'hidden-number-input'
39548         };
39549         
39550         if (this.name) {
39551             hiddenInput.name = this.name;
39552         }
39553         
39554         this.name = '';
39555         
39556         var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39557         
39558         this.name = hiddenInput.name;
39559         
39560         if(cfg.cn.length > 0) {
39561             cfg.cn.push(hiddenInput);
39562         }
39563         
39564         return cfg;
39565     },
39566
39567     // private
39568     initEvents : function()
39569     {   
39570         Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39571         
39572         var allowed = "0123456789";
39573         
39574         if(this.allowDecimals){
39575             allowed += this.decimalSeparator;
39576         }
39577         
39578         if(this.allowNegative){
39579             allowed += "-";
39580         }
39581         
39582         if(this.thousandsDelimiter) {
39583             allowed += ",";
39584         }
39585         
39586         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39587         
39588         var keyPress = function(e){
39589             
39590             var k = e.getKey();
39591             
39592             var c = e.getCharCode();
39593             
39594             if(
39595                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39596                     allowed.indexOf(String.fromCharCode(c)) === -1
39597             ){
39598                 e.stopEvent();
39599                 return;
39600             }
39601             
39602             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39603                 return;
39604             }
39605             
39606             if(allowed.indexOf(String.fromCharCode(c)) === -1){
39607                 e.stopEvent();
39608             }
39609         };
39610         
39611         this.el.on("keypress", keyPress, this);
39612     },
39613     
39614     validateValue : function(value)
39615     {
39616         
39617         if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39618             return false;
39619         }
39620         
39621         var num = this.parseValue(value);
39622         
39623         if(isNaN(num)){
39624             this.markInvalid(String.format(this.nanText, value));
39625             return false;
39626         }
39627         
39628         if(num < this.minValue){
39629             this.markInvalid(String.format(this.minText, this.minValue));
39630             return false;
39631         }
39632         
39633         if(num > this.maxValue){
39634             this.markInvalid(String.format(this.maxText, this.maxValue));
39635             return false;
39636         }
39637         
39638         return true;
39639     },
39640
39641     getValue : function()
39642     {
39643         var v = this.hiddenEl().getValue();
39644         
39645         return this.fixPrecision(this.parseValue(v));
39646     },
39647
39648     parseValue : function(value)
39649     {
39650         if(this.thousandsDelimiter) {
39651             value += "";
39652             r = new RegExp(",", "g");
39653             value = value.replace(r, "");
39654         }
39655         
39656         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39657         return isNaN(value) ? '' : value;
39658     },
39659
39660     fixPrecision : function(value)
39661     {
39662         if(this.thousandsDelimiter) {
39663             value += "";
39664             r = new RegExp(",", "g");
39665             value = value.replace(r, "");
39666         }
39667         
39668         var nan = isNaN(value);
39669         
39670         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39671             return nan ? '' : value;
39672         }
39673         return parseFloat(value).toFixed(this.decimalPrecision);
39674     },
39675
39676     setValue : function(v)
39677     {
39678         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39679         
39680         this.value = v;
39681         
39682         if(this.rendered){
39683             
39684             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39685             
39686             this.inputEl().dom.value = (v == '') ? '' :
39687                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39688             
39689             if(!this.allowZero && v === '0') {
39690                 this.hiddenEl().dom.value = '';
39691                 this.inputEl().dom.value = '';
39692             }
39693             
39694             this.validate();
39695         }
39696     },
39697
39698     decimalPrecisionFcn : function(v)
39699     {
39700         return Math.floor(v);
39701     },
39702
39703     beforeBlur : function()
39704     {
39705         var v = this.parseValue(this.getRawValue());
39706         
39707         if(v || v === 0 || v === ''){
39708             this.setValue(v);
39709         }
39710     },
39711     
39712     hiddenEl : function()
39713     {
39714         return this.el.select('input.hidden-number-input',true).first();
39715     }
39716     
39717 });
39718
39719  
39720
39721 /*
39722 * Licence: LGPL
39723 */
39724
39725 /**
39726  * @class Roo.bootstrap.DocumentSlider
39727  * @extends Roo.bootstrap.Component
39728  * Bootstrap DocumentSlider class
39729  * 
39730  * @constructor
39731  * Create a new DocumentViewer
39732  * @param {Object} config The config object
39733  */
39734
39735 Roo.bootstrap.DocumentSlider = function(config){
39736     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39737     
39738     this.files = [];
39739     
39740     this.addEvents({
39741         /**
39742          * @event initial
39743          * Fire after initEvent
39744          * @param {Roo.bootstrap.DocumentSlider} this
39745          */
39746         "initial" : true,
39747         /**
39748          * @event update
39749          * Fire after update
39750          * @param {Roo.bootstrap.DocumentSlider} this
39751          */
39752         "update" : true,
39753         /**
39754          * @event click
39755          * Fire after click
39756          * @param {Roo.bootstrap.DocumentSlider} this
39757          */
39758         "click" : true
39759     });
39760 };
39761
39762 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
39763     
39764     files : false,
39765     
39766     indicator : 0,
39767     
39768     getAutoCreate : function()
39769     {
39770         var cfg = {
39771             tag : 'div',
39772             cls : 'roo-document-slider',
39773             cn : [
39774                 {
39775                     tag : 'div',
39776                     cls : 'roo-document-slider-header',
39777                     cn : [
39778                         {
39779                             tag : 'div',
39780                             cls : 'roo-document-slider-header-title'
39781                         }
39782                     ]
39783                 },
39784                 {
39785                     tag : 'div',
39786                     cls : 'roo-document-slider-body',
39787                     cn : [
39788                         {
39789                             tag : 'div',
39790                             cls : 'roo-document-slider-prev',
39791                             cn : [
39792                                 {
39793                                     tag : 'i',
39794                                     cls : 'fa fa-chevron-left'
39795                                 }
39796                             ]
39797                         },
39798                         {
39799                             tag : 'div',
39800                             cls : 'roo-document-slider-thumb',
39801                             cn : [
39802                                 {
39803                                     tag : 'img',
39804                                     cls : 'roo-document-slider-image'
39805                                 }
39806                             ]
39807                         },
39808                         {
39809                             tag : 'div',
39810                             cls : 'roo-document-slider-next',
39811                             cn : [
39812                                 {
39813                                     tag : 'i',
39814                                     cls : 'fa fa-chevron-right'
39815                                 }
39816                             ]
39817                         }
39818                     ]
39819                 }
39820             ]
39821         };
39822         
39823         return cfg;
39824     },
39825     
39826     initEvents : function()
39827     {
39828         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39829         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39830         
39831         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39832         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39833         
39834         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39835         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39836         
39837         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39838         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39839         
39840         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39841         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39842         
39843         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39844         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39845         
39846         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39847         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39848         
39849         this.thumbEl.on('click', this.onClick, this);
39850         
39851         this.prevIndicator.on('click', this.prev, this);
39852         
39853         this.nextIndicator.on('click', this.next, this);
39854         
39855     },
39856     
39857     initial : function()
39858     {
39859         if(this.files.length){
39860             this.indicator = 1;
39861             this.update()
39862         }
39863         
39864         this.fireEvent('initial', this);
39865     },
39866     
39867     update : function()
39868     {
39869         this.imageEl.attr('src', this.files[this.indicator - 1]);
39870         
39871         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39872         
39873         this.prevIndicator.show();
39874         
39875         if(this.indicator == 1){
39876             this.prevIndicator.hide();
39877         }
39878         
39879         this.nextIndicator.show();
39880         
39881         if(this.indicator == this.files.length){
39882             this.nextIndicator.hide();
39883         }
39884         
39885         this.thumbEl.scrollTo('top');
39886         
39887         this.fireEvent('update', this);
39888     },
39889     
39890     onClick : function(e)
39891     {
39892         e.preventDefault();
39893         
39894         this.fireEvent('click', this);
39895     },
39896     
39897     prev : function(e)
39898     {
39899         e.preventDefault();
39900         
39901         this.indicator = Math.max(1, this.indicator - 1);
39902         
39903         this.update();
39904     },
39905     
39906     next : function(e)
39907     {
39908         e.preventDefault();
39909         
39910         this.indicator = Math.min(this.files.length, this.indicator + 1);
39911         
39912         this.update();
39913     }
39914 });
39915 /*
39916  * - LGPL
39917  *
39918  * RadioSet
39919  *
39920  *
39921  */
39922
39923 /**
39924  * @class Roo.bootstrap.form.RadioSet
39925  * @extends Roo.bootstrap.form.Input
39926  * @children Roo.bootstrap.form.Radio
39927  * Bootstrap RadioSet class
39928  * @cfg {String} indicatorpos (left|right) default left
39929  * @cfg {Boolean} inline (true|false) inline the element (default true)
39930  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39931  * @constructor
39932  * Create a new RadioSet
39933  * @param {Object} config The config object
39934  */
39935
39936 Roo.bootstrap.form.RadioSet = function(config){
39937     
39938     Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39939     
39940     this.radioes = [];
39941     
39942     Roo.bootstrap.form.RadioSet.register(this);
39943     
39944     this.addEvents({
39945         /**
39946         * @event check
39947         * Fires when the element is checked or unchecked.
39948         * @param {Roo.bootstrap.form.RadioSet} this This radio
39949         * @param {Roo.bootstrap.form.Radio} item The checked item
39950         */
39951        check : true,
39952        /**
39953         * @event click
39954         * Fires when the element is click.
39955         * @param {Roo.bootstrap.form.RadioSet} this This radio set
39956         * @param {Roo.bootstrap.form.Radio} item The checked item
39957         * @param {Roo.EventObject} e The event object
39958         */
39959        click : true
39960     });
39961     
39962 };
39963
39964 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input,  {
39965
39966     radioes : false,
39967     
39968     inline : true,
39969     
39970     weight : '',
39971     
39972     indicatorpos : 'left',
39973     
39974     getAutoCreate : function()
39975     {
39976         var label = {
39977             tag : 'label',
39978             cls : 'roo-radio-set-label',
39979             cn : [
39980                 {
39981                     tag : 'span',
39982                     html : this.fieldLabel
39983                 }
39984             ]
39985         };
39986         if (Roo.bootstrap.version == 3) {
39987             
39988             
39989             if(this.indicatorpos == 'left'){
39990                 label.cn.unshift({
39991                     tag : 'i',
39992                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39993                     tooltip : 'This field is required'
39994                 });
39995             } else {
39996                 label.cn.push({
39997                     tag : 'i',
39998                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39999                     tooltip : 'This field is required'
40000                 });
40001             }
40002         }
40003         var items = {
40004             tag : 'div',
40005             cls : 'roo-radio-set-items'
40006         };
40007         
40008         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
40009         
40010         if (align === 'left' && this.fieldLabel.length) {
40011             
40012             items = {
40013                 cls : "roo-radio-set-right", 
40014                 cn: [
40015                     items
40016                 ]
40017             };
40018             
40019             if(this.labelWidth > 12){
40020                 label.style = "width: " + this.labelWidth + 'px';
40021             }
40022             
40023             if(this.labelWidth < 13 && this.labelmd == 0){
40024                 this.labelmd = this.labelWidth;
40025             }
40026             
40027             if(this.labellg > 0){
40028                 label.cls += ' col-lg-' + this.labellg;
40029                 items.cls += ' col-lg-' + (12 - this.labellg);
40030             }
40031             
40032             if(this.labelmd > 0){
40033                 label.cls += ' col-md-' + this.labelmd;
40034                 items.cls += ' col-md-' + (12 - this.labelmd);
40035             }
40036             
40037             if(this.labelsm > 0){
40038                 label.cls += ' col-sm-' + this.labelsm;
40039                 items.cls += ' col-sm-' + (12 - this.labelsm);
40040             }
40041             
40042             if(this.labelxs > 0){
40043                 label.cls += ' col-xs-' + this.labelxs;
40044                 items.cls += ' col-xs-' + (12 - this.labelxs);
40045             }
40046         }
40047         
40048         var cfg = {
40049             tag : 'div',
40050             cls : 'roo-radio-set',
40051             cn : [
40052                 {
40053                     tag : 'input',
40054                     cls : 'roo-radio-set-input',
40055                     type : 'hidden',
40056                     name : this.name,
40057                     value : this.value ? this.value :  ''
40058                 },
40059                 label,
40060                 items
40061             ]
40062         };
40063         
40064         if(this.weight.length){
40065             cfg.cls += ' roo-radio-' + this.weight;
40066         }
40067         
40068         if(this.inline) {
40069             cfg.cls += ' roo-radio-set-inline';
40070         }
40071         
40072         var settings=this;
40073         ['xs','sm','md','lg'].map(function(size){
40074             if (settings[size]) {
40075                 cfg.cls += ' col-' + size + '-' + settings[size];
40076             }
40077         });
40078         
40079         return cfg;
40080         
40081     },
40082
40083     initEvents : function()
40084     {
40085         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
40086         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
40087         
40088         if(!this.fieldLabel.length){
40089             this.labelEl.hide();
40090         }
40091         
40092         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
40093         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
40094         
40095         this.indicator = this.indicatorEl();
40096         
40097         if(this.indicator){
40098             this.indicator.addClass('invisible');
40099         }
40100         
40101         this.originalValue = this.getValue();
40102         
40103     },
40104     
40105     inputEl: function ()
40106     {
40107         return this.el.select('.roo-radio-set-input', true).first();
40108     },
40109     
40110     getChildContainer : function()
40111     {
40112         return this.itemsEl;
40113     },
40114     
40115     register : function(item)
40116     {
40117         this.radioes.push(item);
40118         
40119     },
40120     
40121     validate : function()
40122     {   
40123         if(this.getVisibilityEl().hasClass('hidden')){
40124             return true;
40125         }
40126         
40127         var valid = false;
40128         
40129         Roo.each(this.radioes, function(i){
40130             if(!i.checked){
40131                 return;
40132             }
40133             
40134             valid = true;
40135             return false;
40136         });
40137         
40138         if(this.allowBlank) {
40139             return true;
40140         }
40141         
40142         if(this.disabled || valid){
40143             this.markValid();
40144             return true;
40145         }
40146         
40147         this.markInvalid();
40148         return false;
40149         
40150     },
40151     
40152     markValid : function()
40153     {
40154         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40155             this.indicatorEl().removeClass('visible');
40156             this.indicatorEl().addClass('invisible');
40157         }
40158         
40159         
40160         if (Roo.bootstrap.version == 3) {
40161             this.el.removeClass([this.invalidClass, this.validClass]);
40162             this.el.addClass(this.validClass);
40163         } else {
40164             this.el.removeClass(['is-invalid','is-valid']);
40165             this.el.addClass(['is-valid']);
40166         }
40167         this.fireEvent('valid', this);
40168     },
40169     
40170     markInvalid : function(msg)
40171     {
40172         if(this.allowBlank || this.disabled){
40173             return;
40174         }
40175         
40176         if(this.labelEl.isVisible(true) && this.indicatorEl()){
40177             this.indicatorEl().removeClass('invisible');
40178             this.indicatorEl().addClass('visible');
40179         }
40180         if (Roo.bootstrap.version == 3) {
40181             this.el.removeClass([this.invalidClass, this.validClass]);
40182             this.el.addClass(this.invalidClass);
40183         } else {
40184             this.el.removeClass(['is-invalid','is-valid']);
40185             this.el.addClass(['is-invalid']);
40186         }
40187         
40188         this.fireEvent('invalid', this, msg);
40189         
40190     },
40191     
40192     setValue : function(v, suppressEvent)
40193     {   
40194         if(this.value === v){
40195             return;
40196         }
40197         
40198         this.value = v;
40199         
40200         if(this.rendered){
40201             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40202         }
40203         
40204         Roo.each(this.radioes, function(i){
40205             i.checked = false;
40206             i.el.removeClass('checked');
40207         });
40208         
40209         Roo.each(this.radioes, function(i){
40210             
40211             if(i.value === v || i.value.toString() === v.toString()){
40212                 i.checked = true;
40213                 i.el.addClass('checked');
40214                 
40215                 if(suppressEvent !== true){
40216                     this.fireEvent('check', this, i);
40217                 }
40218                 
40219                 return false;
40220             }
40221             
40222         }, this);
40223         
40224         this.validate();
40225     },
40226     
40227     clearInvalid : function(){
40228         
40229         if(!this.el || this.preventMark){
40230             return;
40231         }
40232         
40233         this.el.removeClass([this.invalidClass]);
40234         
40235         this.fireEvent('valid', this);
40236     }
40237     
40238 });
40239
40240 Roo.apply(Roo.bootstrap.form.RadioSet, {
40241     
40242     groups: {},
40243     
40244     register : function(set)
40245     {
40246         this.groups[set.name] = set;
40247     },
40248     
40249     get: function(name) 
40250     {
40251         if (typeof(this.groups[name]) == 'undefined') {
40252             return false;
40253         }
40254         
40255         return this.groups[name] ;
40256     }
40257     
40258 });
40259 /*
40260  * Based on:
40261  * Ext JS Library 1.1.1
40262  * Copyright(c) 2006-2007, Ext JS, LLC.
40263  *
40264  * Originally Released Under LGPL - original licence link has changed is not relivant.
40265  *
40266  * Fork - LGPL
40267  * <script type="text/javascript">
40268  */
40269
40270
40271 /**
40272  * @class Roo.bootstrap.SplitBar
40273  * @extends Roo.util.Observable
40274  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40275  * <br><br>
40276  * Usage:
40277  * <pre><code>
40278 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40279                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40280 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40281 split.minSize = 100;
40282 split.maxSize = 600;
40283 split.animate = true;
40284 split.on('moved', splitterMoved);
40285 </code></pre>
40286  * @constructor
40287  * Create a new SplitBar
40288  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
40289  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
40290  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40291  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
40292                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40293                         position of the SplitBar).
40294  */
40295 Roo.bootstrap.SplitBar = function(cfg){
40296     
40297     /** @private */
40298     
40299     //{
40300     //  dragElement : elm
40301     //  resizingElement: el,
40302         // optional..
40303     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40304     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
40305         // existingProxy ???
40306     //}
40307     
40308     this.el = Roo.get(cfg.dragElement, true);
40309     this.el.dom.unselectable = "on";
40310     /** @private */
40311     this.resizingEl = Roo.get(cfg.resizingElement, true);
40312
40313     /**
40314      * @private
40315      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40316      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40317      * @type Number
40318      */
40319     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40320     
40321     /**
40322      * The minimum size of the resizing element. (Defaults to 0)
40323      * @type Number
40324      */
40325     this.minSize = 0;
40326     
40327     /**
40328      * The maximum size of the resizing element. (Defaults to 2000)
40329      * @type Number
40330      */
40331     this.maxSize = 2000;
40332     
40333     /**
40334      * Whether to animate the transition to the new size
40335      * @type Boolean
40336      */
40337     this.animate = false;
40338     
40339     /**
40340      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40341      * @type Boolean
40342      */
40343     this.useShim = false;
40344     
40345     /** @private */
40346     this.shim = null;
40347     
40348     if(!cfg.existingProxy){
40349         /** @private */
40350         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40351     }else{
40352         this.proxy = Roo.get(cfg.existingProxy).dom;
40353     }
40354     /** @private */
40355     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40356     
40357     /** @private */
40358     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40359     
40360     /** @private */
40361     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40362     
40363     /** @private */
40364     this.dragSpecs = {};
40365     
40366     /**
40367      * @private The adapter to use to positon and resize elements
40368      */
40369     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40370     this.adapter.init(this);
40371     
40372     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40373         /** @private */
40374         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40375         this.el.addClass("roo-splitbar-h");
40376     }else{
40377         /** @private */
40378         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40379         this.el.addClass("roo-splitbar-v");
40380     }
40381     
40382     this.addEvents({
40383         /**
40384          * @event resize
40385          * Fires when the splitter is moved (alias for {@link #event-moved})
40386          * @param {Roo.bootstrap.SplitBar} this
40387          * @param {Number} newSize the new width or height
40388          */
40389         "resize" : true,
40390         /**
40391          * @event moved
40392          * Fires when the splitter is moved
40393          * @param {Roo.bootstrap.SplitBar} this
40394          * @param {Number} newSize the new width or height
40395          */
40396         "moved" : true,
40397         /**
40398          * @event beforeresize
40399          * Fires before the splitter is dragged
40400          * @param {Roo.bootstrap.SplitBar} this
40401          */
40402         "beforeresize" : true,
40403
40404         "beforeapply" : true
40405     });
40406
40407     Roo.util.Observable.call(this);
40408 };
40409
40410 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40411     onStartProxyDrag : function(x, y){
40412         this.fireEvent("beforeresize", this);
40413         if(!this.overlay){
40414             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
40415             o.unselectable();
40416             o.enableDisplayMode("block");
40417             // all splitbars share the same overlay
40418             Roo.bootstrap.SplitBar.prototype.overlay = o;
40419         }
40420         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40421         this.overlay.show();
40422         Roo.get(this.proxy).setDisplayed("block");
40423         var size = this.adapter.getElementSize(this);
40424         this.activeMinSize = this.getMinimumSize();;
40425         this.activeMaxSize = this.getMaximumSize();;
40426         var c1 = size - this.activeMinSize;
40427         var c2 = Math.max(this.activeMaxSize - size, 0);
40428         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40429             this.dd.resetConstraints();
40430             this.dd.setXConstraint(
40431                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
40432                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40433             );
40434             this.dd.setYConstraint(0, 0);
40435         }else{
40436             this.dd.resetConstraints();
40437             this.dd.setXConstraint(0, 0);
40438             this.dd.setYConstraint(
40439                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
40440                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40441             );
40442          }
40443         this.dragSpecs.startSize = size;
40444         this.dragSpecs.startPoint = [x, y];
40445         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40446     },
40447     
40448     /** 
40449      * @private Called after the drag operation by the DDProxy
40450      */
40451     onEndProxyDrag : function(e){
40452         Roo.get(this.proxy).setDisplayed(false);
40453         var endPoint = Roo.lib.Event.getXY(e);
40454         if(this.overlay){
40455             this.overlay.hide();
40456         }
40457         var newSize;
40458         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40459             newSize = this.dragSpecs.startSize + 
40460                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40461                     endPoint[0] - this.dragSpecs.startPoint[0] :
40462                     this.dragSpecs.startPoint[0] - endPoint[0]
40463                 );
40464         }else{
40465             newSize = this.dragSpecs.startSize + 
40466                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40467                     endPoint[1] - this.dragSpecs.startPoint[1] :
40468                     this.dragSpecs.startPoint[1] - endPoint[1]
40469                 );
40470         }
40471         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40472         if(newSize != this.dragSpecs.startSize){
40473             if(this.fireEvent('beforeapply', this, newSize) !== false){
40474                 this.adapter.setElementSize(this, newSize);
40475                 this.fireEvent("moved", this, newSize);
40476                 this.fireEvent("resize", this, newSize);
40477             }
40478         }
40479     },
40480     
40481     /**
40482      * Get the adapter this SplitBar uses
40483      * @return The adapter object
40484      */
40485     getAdapter : function(){
40486         return this.adapter;
40487     },
40488     
40489     /**
40490      * Set the adapter this SplitBar uses
40491      * @param {Object} adapter A SplitBar adapter object
40492      */
40493     setAdapter : function(adapter){
40494         this.adapter = adapter;
40495         this.adapter.init(this);
40496     },
40497     
40498     /**
40499      * Gets the minimum size for the resizing element
40500      * @return {Number} The minimum size
40501      */
40502     getMinimumSize : function(){
40503         return this.minSize;
40504     },
40505     
40506     /**
40507      * Sets the minimum size for the resizing element
40508      * @param {Number} minSize The minimum size
40509      */
40510     setMinimumSize : function(minSize){
40511         this.minSize = minSize;
40512     },
40513     
40514     /**
40515      * Gets the maximum size for the resizing element
40516      * @return {Number} The maximum size
40517      */
40518     getMaximumSize : function(){
40519         return this.maxSize;
40520     },
40521     
40522     /**
40523      * Sets the maximum size for the resizing element
40524      * @param {Number} maxSize The maximum size
40525      */
40526     setMaximumSize : function(maxSize){
40527         this.maxSize = maxSize;
40528     },
40529     
40530     /**
40531      * Sets the initialize size for the resizing element
40532      * @param {Number} size The initial size
40533      */
40534     setCurrentSize : function(size){
40535         var oldAnimate = this.animate;
40536         this.animate = false;
40537         this.adapter.setElementSize(this, size);
40538         this.animate = oldAnimate;
40539     },
40540     
40541     /**
40542      * Destroy this splitbar. 
40543      * @param {Boolean} removeEl True to remove the element
40544      */
40545     destroy : function(removeEl){
40546         if(this.shim){
40547             this.shim.remove();
40548         }
40549         this.dd.unreg();
40550         this.proxy.parentNode.removeChild(this.proxy);
40551         if(removeEl){
40552             this.el.remove();
40553         }
40554     }
40555 });
40556
40557 /**
40558  * @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.
40559  */
40560 Roo.bootstrap.SplitBar.createProxy = function(dir){
40561     var proxy = new Roo.Element(document.createElement("div"));
40562     proxy.unselectable();
40563     var cls = 'roo-splitbar-proxy';
40564     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40565     document.body.appendChild(proxy.dom);
40566     return proxy.dom;
40567 };
40568
40569 /** 
40570  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40571  * Default Adapter. It assumes the splitter and resizing element are not positioned
40572  * elements and only gets/sets the width of the element. Generally used for table based layouts.
40573  */
40574 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40575 };
40576
40577 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40578     // do nothing for now
40579     init : function(s){
40580     
40581     },
40582     /**
40583      * Called before drag operations to get the current size of the resizing element. 
40584      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40585      */
40586      getElementSize : function(s){
40587         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40588             return s.resizingEl.getWidth();
40589         }else{
40590             return s.resizingEl.getHeight();
40591         }
40592     },
40593     
40594     /**
40595      * Called after drag operations to set the size of the resizing element.
40596      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40597      * @param {Number} newSize The new size to set
40598      * @param {Function} onComplete A function to be invoked when resizing is complete
40599      */
40600     setElementSize : function(s, newSize, onComplete){
40601         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40602             if(!s.animate){
40603                 s.resizingEl.setWidth(newSize);
40604                 if(onComplete){
40605                     onComplete(s, newSize);
40606                 }
40607             }else{
40608                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40609             }
40610         }else{
40611             
40612             if(!s.animate){
40613                 s.resizingEl.setHeight(newSize);
40614                 if(onComplete){
40615                     onComplete(s, newSize);
40616                 }
40617             }else{
40618                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40619             }
40620         }
40621     }
40622 };
40623
40624 /** 
40625  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40626  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40627  * Adapter that  moves the splitter element to align with the resized sizing element. 
40628  * Used with an absolute positioned SplitBar.
40629  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40630  * document.body, make sure you assign an id to the body element.
40631  */
40632 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40633     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40634     this.container = Roo.get(container);
40635 };
40636
40637 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40638     init : function(s){
40639         this.basic.init(s);
40640     },
40641     
40642     getElementSize : function(s){
40643         return this.basic.getElementSize(s);
40644     },
40645     
40646     setElementSize : function(s, newSize, onComplete){
40647         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40648     },
40649     
40650     moveSplitter : function(s){
40651         var yes = Roo.bootstrap.SplitBar;
40652         switch(s.placement){
40653             case yes.LEFT:
40654                 s.el.setX(s.resizingEl.getRight());
40655                 break;
40656             case yes.RIGHT:
40657                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40658                 break;
40659             case yes.TOP:
40660                 s.el.setY(s.resizingEl.getBottom());
40661                 break;
40662             case yes.BOTTOM:
40663                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40664                 break;
40665         }
40666     }
40667 };
40668
40669 /**
40670  * Orientation constant - Create a vertical SplitBar
40671  * @static
40672  * @type Number
40673  */
40674 Roo.bootstrap.SplitBar.VERTICAL = 1;
40675
40676 /**
40677  * Orientation constant - Create a horizontal SplitBar
40678  * @static
40679  * @type Number
40680  */
40681 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40682
40683 /**
40684  * Placement constant - The resizing element is to the left of the splitter element
40685  * @static
40686  * @type Number
40687  */
40688 Roo.bootstrap.SplitBar.LEFT = 1;
40689
40690 /**
40691  * Placement constant - The resizing element is to the right of the splitter element
40692  * @static
40693  * @type Number
40694  */
40695 Roo.bootstrap.SplitBar.RIGHT = 2;
40696
40697 /**
40698  * Placement constant - The resizing element is positioned above the splitter element
40699  * @static
40700  * @type Number
40701  */
40702 Roo.bootstrap.SplitBar.TOP = 3;
40703
40704 /**
40705  * Placement constant - The resizing element is positioned under splitter element
40706  * @static
40707  * @type Number
40708  */
40709 Roo.bootstrap.SplitBar.BOTTOM = 4;
40710 /*
40711  * Based on:
40712  * Ext JS Library 1.1.1
40713  * Copyright(c) 2006-2007, Ext JS, LLC.
40714  *
40715  * Originally Released Under LGPL - original licence link has changed is not relivant.
40716  *
40717  * Fork - LGPL
40718  * <script type="text/javascript">
40719  */
40720
40721 /**
40722  * @class Roo.bootstrap.layout.Manager
40723  * @extends Roo.bootstrap.Component
40724  * @abstract
40725  * Base class for layout managers.
40726  */
40727 Roo.bootstrap.layout.Manager = function(config)
40728 {
40729     this.monitorWindowResize = true; // do this before we apply configuration.
40730     
40731     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40732
40733
40734
40735
40736
40737     /** false to disable window resize monitoring @type Boolean */
40738     
40739     this.regions = {};
40740     this.addEvents({
40741         /**
40742          * @event layout
40743          * Fires when a layout is performed.
40744          * @param {Roo.LayoutManager} this
40745          */
40746         "layout" : true,
40747         /**
40748          * @event regionresized
40749          * Fires when the user resizes a region.
40750          * @param {Roo.LayoutRegion} region The resized region
40751          * @param {Number} newSize The new size (width for east/west, height for north/south)
40752          */
40753         "regionresized" : true,
40754         /**
40755          * @event regioncollapsed
40756          * Fires when a region is collapsed.
40757          * @param {Roo.LayoutRegion} region The collapsed region
40758          */
40759         "regioncollapsed" : true,
40760         /**
40761          * @event regionexpanded
40762          * Fires when a region is expanded.
40763          * @param {Roo.LayoutRegion} region The expanded region
40764          */
40765         "regionexpanded" : true
40766     });
40767     this.updating = false;
40768
40769     if (config.el) {
40770         this.el = Roo.get(config.el);
40771         this.initEvents();
40772     }
40773
40774 };
40775
40776 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40777
40778
40779     regions : null,
40780
40781     monitorWindowResize : true,
40782
40783
40784     updating : false,
40785
40786
40787     onRender : function(ct, position)
40788     {
40789         if(!this.el){
40790             this.el = Roo.get(ct);
40791             this.initEvents();
40792         }
40793         //this.fireEvent('render',this);
40794     },
40795
40796
40797     initEvents: function()
40798     {
40799
40800
40801         // ie scrollbar fix
40802         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40803             document.body.scroll = "no";
40804         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40805             this.el.position('relative');
40806         }
40807         this.id = this.el.id;
40808         this.el.addClass("roo-layout-container");
40809         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40810         if(this.el.dom != document.body ) {
40811             this.el.on('resize', this.layout,this);
40812             this.el.on('show', this.layout,this);
40813         }
40814
40815     },
40816
40817     /**
40818      * Returns true if this layout is currently being updated
40819      * @return {Boolean}
40820      */
40821     isUpdating : function(){
40822         return this.updating;
40823     },
40824
40825     /**
40826      * Suspend the LayoutManager from doing auto-layouts while
40827      * making multiple add or remove calls
40828      */
40829     beginUpdate : function(){
40830         this.updating = true;
40831     },
40832
40833     /**
40834      * Restore auto-layouts and optionally disable the manager from performing a layout
40835      * @param {Boolean} noLayout true to disable a layout update
40836      */
40837     endUpdate : function(noLayout){
40838         this.updating = false;
40839         if(!noLayout){
40840             this.layout();
40841         }
40842     },
40843
40844     layout: function(){
40845         // abstract...
40846     },
40847
40848     onRegionResized : function(region, newSize){
40849         this.fireEvent("regionresized", region, newSize);
40850         this.layout();
40851     },
40852
40853     onRegionCollapsed : function(region){
40854         this.fireEvent("regioncollapsed", region);
40855     },
40856
40857     onRegionExpanded : function(region){
40858         this.fireEvent("regionexpanded", region);
40859     },
40860
40861     /**
40862      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40863      * performs box-model adjustments.
40864      * @return {Object} The size as an object {width: (the width), height: (the height)}
40865      */
40866     getViewSize : function()
40867     {
40868         var size;
40869         if(this.el.dom != document.body){
40870             size = this.el.getSize();
40871         }else{
40872             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40873         }
40874         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40875         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40876         return size;
40877     },
40878
40879     /**
40880      * Returns the Element this layout is bound to.
40881      * @return {Roo.Element}
40882      */
40883     getEl : function(){
40884         return this.el;
40885     },
40886
40887     /**
40888      * Returns the specified region.
40889      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40890      * @return {Roo.LayoutRegion}
40891      */
40892     getRegion : function(target){
40893         return this.regions[target.toLowerCase()];
40894     },
40895
40896     onWindowResize : function(){
40897         if(this.monitorWindowResize){
40898             this.layout();
40899         }
40900     }
40901 });
40902 /*
40903  * Based on:
40904  * Ext JS Library 1.1.1
40905  * Copyright(c) 2006-2007, Ext JS, LLC.
40906  *
40907  * Originally Released Under LGPL - original licence link has changed is not relivant.
40908  *
40909  * Fork - LGPL
40910  * <script type="text/javascript">
40911  */
40912 /**
40913  * @class Roo.bootstrap.layout.Border
40914  * @extends Roo.bootstrap.layout.Manager
40915  * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40916  * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40917  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40918  * please see: examples/bootstrap/nested.html<br><br>
40919  
40920 <b>The container the layout is rendered into can be either the body element or any other element.
40921 If it is not the body element, the container needs to either be an absolute positioned element,
40922 or you will need to add "position:relative" to the css of the container.  You will also need to specify
40923 the container size if it is not the body element.</b>
40924
40925 * @constructor
40926 * Create a new Border
40927 * @param {Object} config Configuration options
40928  */
40929 Roo.bootstrap.layout.Border = function(config){
40930     config = config || {};
40931     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40932     
40933     
40934     
40935     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40936         if(config[region]){
40937             config[region].region = region;
40938             this.addRegion(config[region]);
40939         }
40940     },this);
40941     
40942 };
40943
40944 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
40945
40946 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40947     
40948         /**
40949          * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40950          */
40951         /**
40952          * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40953          */
40954         /**
40955          * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40956          */
40957         /**
40958          * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40959          */
40960         /**
40961          * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40962          */
40963         
40964         
40965         
40966         
40967     parent : false, // this might point to a 'nest' or a ???
40968     
40969     /**
40970      * Creates and adds a new region if it doesn't already exist.
40971      * @param {String} target The target region key (north, south, east, west or center).
40972      * @param {Object} config The regions config object
40973      * @return {BorderLayoutRegion} The new region
40974      */
40975     addRegion : function(config)
40976     {
40977         if(!this.regions[config.region]){
40978             var r = this.factory(config);
40979             this.bindRegion(r);
40980         }
40981         return this.regions[config.region];
40982     },
40983
40984     // private (kinda)
40985     bindRegion : function(r){
40986         this.regions[r.config.region] = r;
40987         
40988         r.on("visibilitychange",    this.layout, this);
40989         r.on("paneladded",          this.layout, this);
40990         r.on("panelremoved",        this.layout, this);
40991         r.on("invalidated",         this.layout, this);
40992         r.on("resized",             this.onRegionResized, this);
40993         r.on("collapsed",           this.onRegionCollapsed, this);
40994         r.on("expanded",            this.onRegionExpanded, this);
40995     },
40996
40997     /**
40998      * Performs a layout update.
40999      */
41000     layout : function()
41001     {
41002         if(this.updating) {
41003             return;
41004         }
41005         
41006         // render all the rebions if they have not been done alreayd?
41007         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
41008             if(this.regions[region] && !this.regions[region].bodyEl){
41009                 this.regions[region].onRender(this.el)
41010             }
41011         },this);
41012         
41013         var size = this.getViewSize();
41014         var w = size.width;
41015         var h = size.height;
41016         var centerW = w;
41017         var centerH = h;
41018         var centerY = 0;
41019         var centerX = 0;
41020         //var x = 0, y = 0;
41021
41022         var rs = this.regions;
41023         var north = rs["north"];
41024         var south = rs["south"]; 
41025         var west = rs["west"];
41026         var east = rs["east"];
41027         var center = rs["center"];
41028         //if(this.hideOnLayout){ // not supported anymore
41029             //c.el.setStyle("display", "none");
41030         //}
41031         if(north && north.isVisible()){
41032             var b = north.getBox();
41033             var m = north.getMargins();
41034             b.width = w - (m.left+m.right);
41035             b.x = m.left;
41036             b.y = m.top;
41037             centerY = b.height + b.y + m.bottom;
41038             centerH -= centerY;
41039             north.updateBox(this.safeBox(b));
41040         }
41041         if(south && south.isVisible()){
41042             var b = south.getBox();
41043             var m = south.getMargins();
41044             b.width = w - (m.left+m.right);
41045             b.x = m.left;
41046             var totalHeight = (b.height + m.top + m.bottom);
41047             b.y = h - totalHeight + m.top;
41048             centerH -= totalHeight;
41049             south.updateBox(this.safeBox(b));
41050         }
41051         if(west && west.isVisible()){
41052             var b = west.getBox();
41053             var m = west.getMargins();
41054             b.height = centerH - (m.top+m.bottom);
41055             b.x = m.left;
41056             b.y = centerY + m.top;
41057             var totalWidth = (b.width + m.left + m.right);
41058             centerX += totalWidth;
41059             centerW -= totalWidth;
41060             west.updateBox(this.safeBox(b));
41061         }
41062         if(east && east.isVisible()){
41063             var b = east.getBox();
41064             var m = east.getMargins();
41065             b.height = centerH - (m.top+m.bottom);
41066             var totalWidth = (b.width + m.left + m.right);
41067             b.x = w - totalWidth + m.left;
41068             b.y = centerY + m.top;
41069             centerW -= totalWidth;
41070             east.updateBox(this.safeBox(b));
41071         }
41072         if(center){
41073             var m = center.getMargins();
41074             var centerBox = {
41075                 x: centerX + m.left,
41076                 y: centerY + m.top,
41077                 width: centerW - (m.left+m.right),
41078                 height: centerH - (m.top+m.bottom)
41079             };
41080             //if(this.hideOnLayout){
41081                 //center.el.setStyle("display", "block");
41082             //}
41083             center.updateBox(this.safeBox(centerBox));
41084         }
41085         this.el.repaint();
41086         this.fireEvent("layout", this);
41087     },
41088
41089     // private
41090     safeBox : function(box){
41091         box.width = Math.max(0, box.width);
41092         box.height = Math.max(0, box.height);
41093         return box;
41094     },
41095
41096     /**
41097      * Adds a ContentPanel (or subclass) to this layout.
41098      * @param {String} target The target region key (north, south, east, west or center).
41099      * @param {Roo.ContentPanel} panel The panel to add
41100      * @return {Roo.ContentPanel} The added panel
41101      */
41102     add : function(target, panel){
41103          
41104         target = target.toLowerCase();
41105         return this.regions[target].add(panel);
41106     },
41107
41108     /**
41109      * Remove a ContentPanel (or subclass) to this layout.
41110      * @param {String} target The target region key (north, south, east, west or center).
41111      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
41112      * @return {Roo.ContentPanel} The removed panel
41113      */
41114     remove : function(target, panel){
41115         target = target.toLowerCase();
41116         return this.regions[target].remove(panel);
41117     },
41118
41119     /**
41120      * Searches all regions for a panel with the specified id
41121      * @param {String} panelId
41122      * @return {Roo.ContentPanel} The panel or null if it wasn't found
41123      */
41124     findPanel : function(panelId){
41125         var rs = this.regions;
41126         for(var target in rs){
41127             if(typeof rs[target] != "function"){
41128                 var p = rs[target].getPanel(panelId);
41129                 if(p){
41130                     return p;
41131                 }
41132             }
41133         }
41134         return null;
41135     },
41136
41137     /**
41138      * Searches all regions for a panel with the specified id and activates (shows) it.
41139      * @param {String/ContentPanel} panelId The panels id or the panel itself
41140      * @return {Roo.ContentPanel} The shown panel or null
41141      */
41142     showPanel : function(panelId) {
41143       var rs = this.regions;
41144       for(var target in rs){
41145          var r = rs[target];
41146          if(typeof r != "function"){
41147             if(r.hasPanel(panelId)){
41148                return r.showPanel(panelId);
41149             }
41150          }
41151       }
41152       return null;
41153    },
41154
41155    /**
41156      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
41157      * @param {Roo.state.Provider} provider (optional) An alternate state provider
41158      */
41159    /*
41160     restoreState : function(provider){
41161         if(!provider){
41162             provider = Roo.state.Manager;
41163         }
41164         var sm = new Roo.LayoutStateManager();
41165         sm.init(this, provider);
41166     },
41167 */
41168  
41169  
41170     /**
41171      * Adds a xtype elements to the layout.
41172      * <pre><code>
41173
41174 layout.addxtype({
41175        xtype : 'ContentPanel',
41176        region: 'west',
41177        items: [ .... ]
41178    }
41179 );
41180
41181 layout.addxtype({
41182         xtype : 'NestedLayoutPanel',
41183         region: 'west',
41184         layout: {
41185            center: { },
41186            west: { }   
41187         },
41188         items : [ ... list of content panels or nested layout panels.. ]
41189    }
41190 );
41191 </code></pre>
41192      * @param {Object} cfg Xtype definition of item to add.
41193      */
41194     addxtype : function(cfg)
41195     {
41196         // basically accepts a pannel...
41197         // can accept a layout region..!?!?
41198         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41199         
41200         
41201         // theory?  children can only be panels??
41202         
41203         //if (!cfg.xtype.match(/Panel$/)) {
41204         //    return false;
41205         //}
41206         var ret = false;
41207         
41208         if (typeof(cfg.region) == 'undefined') {
41209             Roo.log("Failed to add Panel, region was not set");
41210             Roo.log(cfg);
41211             return false;
41212         }
41213         var region = cfg.region;
41214         delete cfg.region;
41215         
41216           
41217         var xitems = [];
41218         if (cfg.items) {
41219             xitems = cfg.items;
41220             delete cfg.items;
41221         }
41222         var nb = false;
41223         
41224         if ( region == 'center') {
41225             Roo.log("Center: " + cfg.title);
41226         }
41227         
41228         
41229         switch(cfg.xtype) 
41230         {
41231             case 'Content':  // ContentPanel (el, cfg)
41232             case 'Scroll':  // ContentPanel (el, cfg)
41233             case 'View': 
41234                 cfg.autoCreate = cfg.autoCreate || true;
41235                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41236                 //} else {
41237                 //    var el = this.el.createChild();
41238                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41239                 //}
41240                 
41241                 this.add(region, ret);
41242                 break;
41243             
41244             /*
41245             case 'TreePanel': // our new panel!
41246                 cfg.el = this.el.createChild();
41247                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41248                 this.add(region, ret);
41249                 break;
41250             */
41251             
41252             case 'Nest': 
41253                 // create a new Layout (which is  a Border Layout...
41254                 
41255                 var clayout = cfg.layout;
41256                 clayout.el  = this.el.createChild();
41257                 clayout.items   = clayout.items  || [];
41258                 
41259                 delete cfg.layout;
41260                 
41261                 // replace this exitems with the clayout ones..
41262                 xitems = clayout.items;
41263                  
41264                 // force background off if it's in center...
41265                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41266                     cfg.background = false;
41267                 }
41268                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
41269                 
41270                 
41271                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41272                 //console.log('adding nested layout panel '  + cfg.toSource());
41273                 this.add(region, ret);
41274                 nb = {}; /// find first...
41275                 break;
41276             
41277             case 'Grid':
41278                 
41279                 // needs grid and region
41280                 
41281                 //var el = this.getRegion(region).el.createChild();
41282                 /*
41283                  *var el = this.el.createChild();
41284                 // create the grid first...
41285                 cfg.grid.container = el;
41286                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41287                 */
41288                 
41289                 if (region == 'center' && this.active ) {
41290                     cfg.background = false;
41291                 }
41292                 
41293                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41294                 
41295                 this.add(region, ret);
41296                 /*
41297                 if (cfg.background) {
41298                     // render grid on panel activation (if panel background)
41299                     ret.on('activate', function(gp) {
41300                         if (!gp.grid.rendered) {
41301                     //        gp.grid.render(el);
41302                         }
41303                     });
41304                 } else {
41305                   //  cfg.grid.render(el);
41306                 }
41307                 */
41308                 break;
41309            
41310            
41311             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41312                 // it was the old xcomponent building that caused this before.
41313                 // espeically if border is the top element in the tree.
41314                 ret = this;
41315                 break; 
41316                 
41317                     
41318                 
41319                 
41320                 
41321             default:
41322                 /*
41323                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41324                     
41325                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41326                     this.add(region, ret);
41327                 } else {
41328                 */
41329                     Roo.log(cfg);
41330                     throw "Can not add '" + cfg.xtype + "' to Border";
41331                     return null;
41332              
41333                                 
41334              
41335         }
41336         this.beginUpdate();
41337         // add children..
41338         var region = '';
41339         var abn = {};
41340         Roo.each(xitems, function(i)  {
41341             region = nb && i.region ? i.region : false;
41342             
41343             var add = ret.addxtype(i);
41344            
41345             if (region) {
41346                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41347                 if (!i.background) {
41348                     abn[region] = nb[region] ;
41349                 }
41350             }
41351             
41352         });
41353         this.endUpdate();
41354
41355         // make the last non-background panel active..
41356         //if (nb) { Roo.log(abn); }
41357         if (nb) {
41358             
41359             for(var r in abn) {
41360                 region = this.getRegion(r);
41361                 if (region) {
41362                     // tried using nb[r], but it does not work..
41363                      
41364                     region.showPanel(abn[r]);
41365                    
41366                 }
41367             }
41368         }
41369         return ret;
41370         
41371     },
41372     
41373     
41374 // private
41375     factory : function(cfg)
41376     {
41377         
41378         var validRegions = Roo.bootstrap.layout.Border.regions;
41379
41380         var target = cfg.region;
41381         cfg.mgr = this;
41382         
41383         var r = Roo.bootstrap.layout;
41384         Roo.log(target);
41385         switch(target){
41386             case "north":
41387                 return new r.North(cfg);
41388             case "south":
41389                 return new r.South(cfg);
41390             case "east":
41391                 return new r.East(cfg);
41392             case "west":
41393                 return new r.West(cfg);
41394             case "center":
41395                 return new r.Center(cfg);
41396         }
41397         throw 'Layout region "'+target+'" not supported.';
41398     }
41399     
41400     
41401 });
41402  /*
41403  * Based on:
41404  * Ext JS Library 1.1.1
41405  * Copyright(c) 2006-2007, Ext JS, LLC.
41406  *
41407  * Originally Released Under LGPL - original licence link has changed is not relivant.
41408  *
41409  * Fork - LGPL
41410  * <script type="text/javascript">
41411  */
41412  
41413 /**
41414  * @class Roo.bootstrap.layout.Basic
41415  * @extends Roo.util.Observable
41416  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41417  * and does not have a titlebar, tabs or any other features. All it does is size and position 
41418  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41419  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41420  * @cfg {string}   region  the region that it inhabits..
41421  * @cfg {bool}   skipConfig skip config?
41422  * 
41423
41424  */
41425 Roo.bootstrap.layout.Basic = function(config){
41426     
41427     this.mgr = config.mgr;
41428     
41429     this.position = config.region;
41430     
41431     var skipConfig = config.skipConfig;
41432     
41433     this.events = {
41434         /**
41435          * @scope Roo.BasicLayoutRegion
41436          */
41437         
41438         /**
41439          * @event beforeremove
41440          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41441          * @param {Roo.LayoutRegion} this
41442          * @param {Roo.ContentPanel} panel The panel
41443          * @param {Object} e The cancel event object
41444          */
41445         "beforeremove" : true,
41446         /**
41447          * @event invalidated
41448          * Fires when the layout for this region is changed.
41449          * @param {Roo.LayoutRegion} this
41450          */
41451         "invalidated" : true,
41452         /**
41453          * @event visibilitychange
41454          * Fires when this region is shown or hidden 
41455          * @param {Roo.LayoutRegion} this
41456          * @param {Boolean} visibility true or false
41457          */
41458         "visibilitychange" : true,
41459         /**
41460          * @event paneladded
41461          * Fires when a panel is added. 
41462          * @param {Roo.LayoutRegion} this
41463          * @param {Roo.ContentPanel} panel The panel
41464          */
41465         "paneladded" : true,
41466         /**
41467          * @event panelremoved
41468          * Fires when a panel is removed. 
41469          * @param {Roo.LayoutRegion} this
41470          * @param {Roo.ContentPanel} panel The panel
41471          */
41472         "panelremoved" : true,
41473         /**
41474          * @event beforecollapse
41475          * Fires when this region before collapse.
41476          * @param {Roo.LayoutRegion} this
41477          */
41478         "beforecollapse" : true,
41479         /**
41480          * @event collapsed
41481          * Fires when this region is collapsed.
41482          * @param {Roo.LayoutRegion} this
41483          */
41484         "collapsed" : true,
41485         /**
41486          * @event expanded
41487          * Fires when this region is expanded.
41488          * @param {Roo.LayoutRegion} this
41489          */
41490         "expanded" : true,
41491         /**
41492          * @event slideshow
41493          * Fires when this region is slid into view.
41494          * @param {Roo.LayoutRegion} this
41495          */
41496         "slideshow" : true,
41497         /**
41498          * @event slidehide
41499          * Fires when this region slides out of view. 
41500          * @param {Roo.LayoutRegion} this
41501          */
41502         "slidehide" : true,
41503         /**
41504          * @event panelactivated
41505          * Fires when a panel is activated. 
41506          * @param {Roo.LayoutRegion} this
41507          * @param {Roo.ContentPanel} panel The activated panel
41508          */
41509         "panelactivated" : true,
41510         /**
41511          * @event resized
41512          * Fires when the user resizes this region. 
41513          * @param {Roo.LayoutRegion} this
41514          * @param {Number} newSize The new size (width for east/west, height for north/south)
41515          */
41516         "resized" : true
41517     };
41518     /** A collection of panels in this region. @type Roo.util.MixedCollection */
41519     this.panels = new Roo.util.MixedCollection();
41520     this.panels.getKey = this.getPanelId.createDelegate(this);
41521     this.box = null;
41522     this.activePanel = null;
41523     // ensure listeners are added...
41524     
41525     if (config.listeners || config.events) {
41526         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41527             listeners : config.listeners || {},
41528             events : config.events || {}
41529         });
41530     }
41531     
41532     if(skipConfig !== true){
41533         this.applyConfig(config);
41534     }
41535 };
41536
41537 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41538 {
41539     getPanelId : function(p){
41540         return p.getId();
41541     },
41542     
41543     applyConfig : function(config){
41544         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41545         this.config = config;
41546         
41547     },
41548     
41549     /**
41550      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
41551      * the width, for horizontal (north, south) the height.
41552      * @param {Number} newSize The new width or height
41553      */
41554     resizeTo : function(newSize){
41555         var el = this.el ? this.el :
41556                  (this.activePanel ? this.activePanel.getEl() : null);
41557         if(el){
41558             switch(this.position){
41559                 case "east":
41560                 case "west":
41561                     el.setWidth(newSize);
41562                     this.fireEvent("resized", this, newSize);
41563                 break;
41564                 case "north":
41565                 case "south":
41566                     el.setHeight(newSize);
41567                     this.fireEvent("resized", this, newSize);
41568                 break;                
41569             }
41570         }
41571     },
41572     
41573     getBox : function(){
41574         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41575     },
41576     
41577     getMargins : function(){
41578         return this.margins;
41579     },
41580     
41581     updateBox : function(box){
41582         this.box = box;
41583         var el = this.activePanel.getEl();
41584         el.dom.style.left = box.x + "px";
41585         el.dom.style.top = box.y + "px";
41586         this.activePanel.setSize(box.width, box.height);
41587     },
41588     
41589     /**
41590      * Returns the container element for this region.
41591      * @return {Roo.Element}
41592      */
41593     getEl : function(){
41594         return this.activePanel;
41595     },
41596     
41597     /**
41598      * Returns true if this region is currently visible.
41599      * @return {Boolean}
41600      */
41601     isVisible : function(){
41602         return this.activePanel ? true : false;
41603     },
41604     
41605     setActivePanel : function(panel){
41606         panel = this.getPanel(panel);
41607         if(this.activePanel && this.activePanel != panel){
41608             this.activePanel.setActiveState(false);
41609             this.activePanel.getEl().setLeftTop(-10000,-10000);
41610         }
41611         this.activePanel = panel;
41612         panel.setActiveState(true);
41613         if(this.box){
41614             panel.setSize(this.box.width, this.box.height);
41615         }
41616         this.fireEvent("panelactivated", this, panel);
41617         this.fireEvent("invalidated");
41618     },
41619     
41620     /**
41621      * Show the specified panel.
41622      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41623      * @return {Roo.ContentPanel} The shown panel or null
41624      */
41625     showPanel : function(panel){
41626         panel = this.getPanel(panel);
41627         if(panel){
41628             this.setActivePanel(panel);
41629         }
41630         return panel;
41631     },
41632     
41633     /**
41634      * Get the active panel for this region.
41635      * @return {Roo.ContentPanel} The active panel or null
41636      */
41637     getActivePanel : function(){
41638         return this.activePanel;
41639     },
41640     
41641     /**
41642      * Add the passed ContentPanel(s)
41643      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41644      * @return {Roo.ContentPanel} The panel added (if only one was added)
41645      */
41646     add : function(panel){
41647         if(arguments.length > 1){
41648             for(var i = 0, len = arguments.length; i < len; i++) {
41649                 this.add(arguments[i]);
41650             }
41651             return null;
41652         }
41653         if(this.hasPanel(panel)){
41654             this.showPanel(panel);
41655             return panel;
41656         }
41657         var el = panel.getEl();
41658         if(el.dom.parentNode != this.mgr.el.dom){
41659             this.mgr.el.dom.appendChild(el.dom);
41660         }
41661         if(panel.setRegion){
41662             panel.setRegion(this);
41663         }
41664         this.panels.add(panel);
41665         el.setStyle("position", "absolute");
41666         if(!panel.background){
41667             this.setActivePanel(panel);
41668             if(this.config.initialSize && this.panels.getCount()==1){
41669                 this.resizeTo(this.config.initialSize);
41670             }
41671         }
41672         this.fireEvent("paneladded", this, panel);
41673         return panel;
41674     },
41675     
41676     /**
41677      * Returns true if the panel is in this region.
41678      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41679      * @return {Boolean}
41680      */
41681     hasPanel : function(panel){
41682         if(typeof panel == "object"){ // must be panel obj
41683             panel = panel.getId();
41684         }
41685         return this.getPanel(panel) ? true : false;
41686     },
41687     
41688     /**
41689      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41690      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41691      * @param {Boolean} preservePanel Overrides the config preservePanel option
41692      * @return {Roo.ContentPanel} The panel that was removed
41693      */
41694     remove : function(panel, preservePanel){
41695         panel = this.getPanel(panel);
41696         if(!panel){
41697             return null;
41698         }
41699         var e = {};
41700         this.fireEvent("beforeremove", this, panel, e);
41701         if(e.cancel === true){
41702             return null;
41703         }
41704         var panelId = panel.getId();
41705         this.panels.removeKey(panelId);
41706         return panel;
41707     },
41708     
41709     /**
41710      * Returns the panel specified or null if it's not in this region.
41711      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41712      * @return {Roo.ContentPanel}
41713      */
41714     getPanel : function(id){
41715         if(typeof id == "object"){ // must be panel obj
41716             return id;
41717         }
41718         return this.panels.get(id);
41719     },
41720     
41721     /**
41722      * Returns this regions position (north/south/east/west/center).
41723      * @return {String} 
41724      */
41725     getPosition: function(){
41726         return this.position;    
41727     }
41728 });/*
41729  * Based on:
41730  * Ext JS Library 1.1.1
41731  * Copyright(c) 2006-2007, Ext JS, LLC.
41732  *
41733  * Originally Released Under LGPL - original licence link has changed is not relivant.
41734  *
41735  * Fork - LGPL
41736  * <script type="text/javascript">
41737  */
41738  
41739 /**
41740  * @class Roo.bootstrap.layout.Region
41741  * @extends Roo.bootstrap.layout.Basic
41742  * This class represents a region in a layout manager.
41743  
41744  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41745  * @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})
41746  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
41747  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
41748  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
41749  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
41750  * @cfg {String}    title           The title for the region (overrides panel titles)
41751  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
41752  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41753  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
41754  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41755  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
41756  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41757  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
41758  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
41759  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
41760  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
41761
41762  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
41763  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
41764  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
41765  * @cfg {Number}    width           For East/West panels
41766  * @cfg {Number}    height          For North/South panels
41767  * @cfg {Boolean}   split           To show the splitter
41768  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
41769  * 
41770  * @cfg {string}   cls             Extra CSS classes to add to region
41771  * 
41772  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
41773  * @cfg {string}   region  the region that it inhabits..
41774  *
41775
41776  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
41777  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
41778
41779  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
41780  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
41781  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
41782  */
41783 Roo.bootstrap.layout.Region = function(config)
41784 {
41785     this.applyConfig(config);
41786
41787     var mgr = config.mgr;
41788     var pos = config.region;
41789     config.skipConfig = true;
41790     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41791     
41792     if (mgr.el) {
41793         this.onRender(mgr.el);   
41794     }
41795      
41796     this.visible = true;
41797     this.collapsed = false;
41798     this.unrendered_panels = [];
41799 };
41800
41801 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41802
41803     position: '', // set by wrapper (eg. north/south etc..)
41804     unrendered_panels : null,  // unrendered panels.
41805     
41806     tabPosition : false,
41807     
41808     mgr: false, // points to 'Border'
41809     
41810     
41811     createBody : function(){
41812         /** This region's body element 
41813         * @type Roo.Element */
41814         this.bodyEl = this.el.createChild({
41815                 tag: "div",
41816                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41817         });
41818     },
41819
41820     onRender: function(ctr, pos)
41821     {
41822         var dh = Roo.DomHelper;
41823         /** This region's container element 
41824         * @type Roo.Element */
41825         this.el = dh.append(ctr.dom, {
41826                 tag: "div",
41827                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41828             }, true);
41829         /** This region's title element 
41830         * @type Roo.Element */
41831     
41832         this.titleEl = dh.append(this.el.dom,  {
41833                 tag: "div",
41834                 unselectable: "on",
41835                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41836                 children:[
41837                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
41838                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41839                 ]
41840             }, true);
41841         
41842         this.titleEl.enableDisplayMode();
41843         /** This region's title text element 
41844         * @type HTMLElement */
41845         this.titleTextEl = this.titleEl.dom.firstChild;
41846         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41847         /*
41848         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41849         this.closeBtn.enableDisplayMode();
41850         this.closeBtn.on("click", this.closeClicked, this);
41851         this.closeBtn.hide();
41852     */
41853         this.createBody(this.config);
41854         if(this.config.hideWhenEmpty){
41855             this.hide();
41856             this.on("paneladded", this.validateVisibility, this);
41857             this.on("panelremoved", this.validateVisibility, this);
41858         }
41859         if(this.autoScroll){
41860             this.bodyEl.setStyle("overflow", "auto");
41861         }else{
41862             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41863         }
41864         //if(c.titlebar !== false){
41865             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41866                 this.titleEl.hide();
41867             }else{
41868                 this.titleEl.show();
41869                 if(this.config.title){
41870                     this.titleTextEl.innerHTML = this.config.title;
41871                 }
41872             }
41873         //}
41874         if(this.config.collapsed){
41875             this.collapse(true);
41876         }
41877         if(this.config.hidden){
41878             this.hide();
41879         }
41880         
41881         if (this.unrendered_panels && this.unrendered_panels.length) {
41882             for (var i =0;i< this.unrendered_panels.length; i++) {
41883                 this.add(this.unrendered_panels[i]);
41884             }
41885             this.unrendered_panels = null;
41886             
41887         }
41888         
41889     },
41890     
41891     applyConfig : function(c)
41892     {
41893         /*
41894          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41895             var dh = Roo.DomHelper;
41896             if(c.titlebar !== false){
41897                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41898                 this.collapseBtn.on("click", this.collapse, this);
41899                 this.collapseBtn.enableDisplayMode();
41900                 /*
41901                 if(c.showPin === true || this.showPin){
41902                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41903                     this.stickBtn.enableDisplayMode();
41904                     this.stickBtn.on("click", this.expand, this);
41905                     this.stickBtn.hide();
41906                 }
41907                 
41908             }
41909             */
41910             /** This region's collapsed element
41911             * @type Roo.Element */
41912             /*
41913              *
41914             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41915                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41916             ]}, true);
41917             
41918             if(c.floatable !== false){
41919                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41920                this.collapsedEl.on("click", this.collapseClick, this);
41921             }
41922
41923             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41924                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41925                    id: "message", unselectable: "on", style:{"float":"left"}});
41926                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41927              }
41928             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41929             this.expandBtn.on("click", this.expand, this);
41930             
41931         }
41932         
41933         if(this.collapseBtn){
41934             this.collapseBtn.setVisible(c.collapsible == true);
41935         }
41936         
41937         this.cmargins = c.cmargins || this.cmargins ||
41938                          (this.position == "west" || this.position == "east" ?
41939                              {top: 0, left: 2, right:2, bottom: 0} :
41940                              {top: 2, left: 0, right:0, bottom: 2});
41941         */
41942         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41943         
41944         
41945         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41946         
41947         this.autoScroll = c.autoScroll || false;
41948         
41949         
41950        
41951         
41952         this.duration = c.duration || .30;
41953         this.slideDuration = c.slideDuration || .45;
41954         this.config = c;
41955        
41956     },
41957     /**
41958      * Returns true if this region is currently visible.
41959      * @return {Boolean}
41960      */
41961     isVisible : function(){
41962         return this.visible;
41963     },
41964
41965     /**
41966      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41967      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
41968      */
41969     //setCollapsedTitle : function(title){
41970     //    title = title || "&#160;";
41971      //   if(this.collapsedTitleTextEl){
41972       //      this.collapsedTitleTextEl.innerHTML = title;
41973        // }
41974     //},
41975
41976     getBox : function(){
41977         var b;
41978       //  if(!this.collapsed){
41979             b = this.el.getBox(false, true);
41980        // }else{
41981           //  b = this.collapsedEl.getBox(false, true);
41982         //}
41983         return b;
41984     },
41985
41986     getMargins : function(){
41987         return this.margins;
41988         //return this.collapsed ? this.cmargins : this.margins;
41989     },
41990 /*
41991     highlight : function(){
41992         this.el.addClass("x-layout-panel-dragover");
41993     },
41994
41995     unhighlight : function(){
41996         this.el.removeClass("x-layout-panel-dragover");
41997     },
41998 */
41999     updateBox : function(box)
42000     {
42001         if (!this.bodyEl) {
42002             return; // not rendered yet..
42003         }
42004         
42005         this.box = box;
42006         if(!this.collapsed){
42007             this.el.dom.style.left = box.x + "px";
42008             this.el.dom.style.top = box.y + "px";
42009             this.updateBody(box.width, box.height);
42010         }else{
42011             this.collapsedEl.dom.style.left = box.x + "px";
42012             this.collapsedEl.dom.style.top = box.y + "px";
42013             this.collapsedEl.setSize(box.width, box.height);
42014         }
42015         if(this.tabs){
42016             this.tabs.autoSizeTabs();
42017         }
42018     },
42019
42020     updateBody : function(w, h)
42021     {
42022         if(w !== null){
42023             this.el.setWidth(w);
42024             w -= this.el.getBorderWidth("rl");
42025             if(this.config.adjustments){
42026                 w += this.config.adjustments[0];
42027             }
42028         }
42029         if(h !== null && h > 0){
42030             this.el.setHeight(h);
42031             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
42032             h -= this.el.getBorderWidth("tb");
42033             if(this.config.adjustments){
42034                 h += this.config.adjustments[1];
42035             }
42036             this.bodyEl.setHeight(h);
42037             if(this.tabs){
42038                 h = this.tabs.syncHeight(h);
42039             }
42040         }
42041         if(this.panelSize){
42042             w = w !== null ? w : this.panelSize.width;
42043             h = h !== null ? h : this.panelSize.height;
42044         }
42045         if(this.activePanel){
42046             var el = this.activePanel.getEl();
42047             w = w !== null ? w : el.getWidth();
42048             h = h !== null ? h : el.getHeight();
42049             this.panelSize = {width: w, height: h};
42050             this.activePanel.setSize(w, h);
42051         }
42052         if(Roo.isIE && this.tabs){
42053             this.tabs.el.repaint();
42054         }
42055     },
42056
42057     /**
42058      * Returns the container element for this region.
42059      * @return {Roo.Element}
42060      */
42061     getEl : function(){
42062         return this.el;
42063     },
42064
42065     /**
42066      * Hides this region.
42067      */
42068     hide : function(){
42069         //if(!this.collapsed){
42070             this.el.dom.style.left = "-2000px";
42071             this.el.hide();
42072         //}else{
42073          //   this.collapsedEl.dom.style.left = "-2000px";
42074          //   this.collapsedEl.hide();
42075        // }
42076         this.visible = false;
42077         this.fireEvent("visibilitychange", this, false);
42078     },
42079
42080     /**
42081      * Shows this region if it was previously hidden.
42082      */
42083     show : function(){
42084         //if(!this.collapsed){
42085             this.el.show();
42086         //}else{
42087         //    this.collapsedEl.show();
42088        // }
42089         this.visible = true;
42090         this.fireEvent("visibilitychange", this, true);
42091     },
42092 /*
42093     closeClicked : function(){
42094         if(this.activePanel){
42095             this.remove(this.activePanel);
42096         }
42097     },
42098
42099     collapseClick : function(e){
42100         if(this.isSlid){
42101            e.stopPropagation();
42102            this.slideIn();
42103         }else{
42104            e.stopPropagation();
42105            this.slideOut();
42106         }
42107     },
42108 */
42109     /**
42110      * Collapses this region.
42111      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
42112      */
42113     /*
42114     collapse : function(skipAnim, skipCheck = false){
42115         if(this.collapsed) {
42116             return;
42117         }
42118         
42119         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
42120             
42121             this.collapsed = true;
42122             if(this.split){
42123                 this.split.el.hide();
42124             }
42125             if(this.config.animate && skipAnim !== true){
42126                 this.fireEvent("invalidated", this);
42127                 this.animateCollapse();
42128             }else{
42129                 this.el.setLocation(-20000,-20000);
42130                 this.el.hide();
42131                 this.collapsedEl.show();
42132                 this.fireEvent("collapsed", this);
42133                 this.fireEvent("invalidated", this);
42134             }
42135         }
42136         
42137     },
42138 */
42139     animateCollapse : function(){
42140         // overridden
42141     },
42142
42143     /**
42144      * Expands this region if it was previously collapsed.
42145      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
42146      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
42147      */
42148     /*
42149     expand : function(e, skipAnim){
42150         if(e) {
42151             e.stopPropagation();
42152         }
42153         if(!this.collapsed || this.el.hasActiveFx()) {
42154             return;
42155         }
42156         if(this.isSlid){
42157             this.afterSlideIn();
42158             skipAnim = true;
42159         }
42160         this.collapsed = false;
42161         if(this.config.animate && skipAnim !== true){
42162             this.animateExpand();
42163         }else{
42164             this.el.show();
42165             if(this.split){
42166                 this.split.el.show();
42167             }
42168             this.collapsedEl.setLocation(-2000,-2000);
42169             this.collapsedEl.hide();
42170             this.fireEvent("invalidated", this);
42171             this.fireEvent("expanded", this);
42172         }
42173     },
42174 */
42175     animateExpand : function(){
42176         // overridden
42177     },
42178
42179     initTabs : function()
42180     {
42181         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
42182         
42183         var ts = new Roo.bootstrap.panel.Tabs({
42184             el: this.bodyEl.dom,
42185             region : this,
42186             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
42187             disableTooltips: this.config.disableTabTips,
42188             toolbar : this.config.toolbar
42189         });
42190         
42191         if(this.config.hideTabs){
42192             ts.stripWrap.setDisplayed(false);
42193         }
42194         this.tabs = ts;
42195         ts.resizeTabs = this.config.resizeTabs === true;
42196         ts.minTabWidth = this.config.minTabWidth || 40;
42197         ts.maxTabWidth = this.config.maxTabWidth || 250;
42198         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42199         ts.monitorResize = false;
42200         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42201         ts.bodyEl.addClass('roo-layout-tabs-body');
42202         this.panels.each(this.initPanelAsTab, this);
42203     },
42204
42205     initPanelAsTab : function(panel){
42206         var ti = this.tabs.addTab(
42207             panel.getEl().id,
42208             panel.getTitle(),
42209             null,
42210             this.config.closeOnTab && panel.isClosable(),
42211             panel.tpl
42212         );
42213         if(panel.tabTip !== undefined){
42214             ti.setTooltip(panel.tabTip);
42215         }
42216         ti.on("activate", function(){
42217               this.setActivePanel(panel);
42218         }, this);
42219         
42220         if(this.config.closeOnTab){
42221             ti.on("beforeclose", function(t, e){
42222                 e.cancel = true;
42223                 this.remove(panel);
42224             }, this);
42225         }
42226         
42227         panel.tabItem = ti;
42228         
42229         return ti;
42230     },
42231
42232     updatePanelTitle : function(panel, title)
42233     {
42234         if(this.activePanel == panel){
42235             this.updateTitle(title);
42236         }
42237         if(this.tabs){
42238             var ti = this.tabs.getTab(panel.getEl().id);
42239             ti.setText(title);
42240             if(panel.tabTip !== undefined){
42241                 ti.setTooltip(panel.tabTip);
42242             }
42243         }
42244     },
42245
42246     updateTitle : function(title){
42247         if(this.titleTextEl && !this.config.title){
42248             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
42249         }
42250     },
42251
42252     setActivePanel : function(panel)
42253     {
42254         panel = this.getPanel(panel);
42255         if(this.activePanel && this.activePanel != panel){
42256             if(this.activePanel.setActiveState(false) === false){
42257                 return;
42258             }
42259         }
42260         this.activePanel = panel;
42261         panel.setActiveState(true);
42262         if(this.panelSize){
42263             panel.setSize(this.panelSize.width, this.panelSize.height);
42264         }
42265         if(this.closeBtn){
42266             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42267         }
42268         this.updateTitle(panel.getTitle());
42269         if(this.tabs){
42270             this.fireEvent("invalidated", this);
42271         }
42272         this.fireEvent("panelactivated", this, panel);
42273     },
42274
42275     /**
42276      * Shows the specified panel.
42277      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42278      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42279      */
42280     showPanel : function(panel)
42281     {
42282         panel = this.getPanel(panel);
42283         if(panel){
42284             if(this.tabs){
42285                 var tab = this.tabs.getTab(panel.getEl().id);
42286                 if(tab.isHidden()){
42287                     this.tabs.unhideTab(tab.id);
42288                 }
42289                 tab.activate();
42290             }else{
42291                 this.setActivePanel(panel);
42292             }
42293         }
42294         return panel;
42295     },
42296
42297     /**
42298      * Get the active panel for this region.
42299      * @return {Roo.ContentPanel} The active panel or null
42300      */
42301     getActivePanel : function(){
42302         return this.activePanel;
42303     },
42304
42305     validateVisibility : function(){
42306         if(this.panels.getCount() < 1){
42307             this.updateTitle("&#160;");
42308             this.closeBtn.hide();
42309             this.hide();
42310         }else{
42311             if(!this.isVisible()){
42312                 this.show();
42313             }
42314         }
42315     },
42316
42317     /**
42318      * Adds the passed ContentPanel(s) to this region.
42319      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42320      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42321      */
42322     add : function(panel)
42323     {
42324         if(arguments.length > 1){
42325             for(var i = 0, len = arguments.length; i < len; i++) {
42326                 this.add(arguments[i]);
42327             }
42328             return null;
42329         }
42330         
42331         // if we have not been rendered yet, then we can not really do much of this..
42332         if (!this.bodyEl) {
42333             this.unrendered_panels.push(panel);
42334             return panel;
42335         }
42336         
42337         
42338         
42339         
42340         if(this.hasPanel(panel)){
42341             this.showPanel(panel);
42342             return panel;
42343         }
42344         panel.setRegion(this);
42345         this.panels.add(panel);
42346        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42347             // sinle panel - no tab...?? would it not be better to render it with the tabs,
42348             // and hide them... ???
42349             this.bodyEl.dom.appendChild(panel.getEl().dom);
42350             if(panel.background !== true){
42351                 this.setActivePanel(panel);
42352             }
42353             this.fireEvent("paneladded", this, panel);
42354             return panel;
42355         }
42356         */
42357         if(!this.tabs){
42358             this.initTabs();
42359         }else{
42360             this.initPanelAsTab(panel);
42361         }
42362         
42363         
42364         if(panel.background !== true){
42365             this.tabs.activate(panel.getEl().id);
42366         }
42367         this.fireEvent("paneladded", this, panel);
42368         return panel;
42369     },
42370
42371     /**
42372      * Hides the tab for the specified panel.
42373      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42374      */
42375     hidePanel : function(panel){
42376         if(this.tabs && (panel = this.getPanel(panel))){
42377             this.tabs.hideTab(panel.getEl().id);
42378         }
42379     },
42380
42381     /**
42382      * Unhides the tab for a previously hidden panel.
42383      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42384      */
42385     unhidePanel : function(panel){
42386         if(this.tabs && (panel = this.getPanel(panel))){
42387             this.tabs.unhideTab(panel.getEl().id);
42388         }
42389     },
42390
42391     clearPanels : function(){
42392         while(this.panels.getCount() > 0){
42393              this.remove(this.panels.first());
42394         }
42395     },
42396
42397     /**
42398      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42399      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42400      * @param {Boolean} preservePanel Overrides the config preservePanel option
42401      * @return {Roo.ContentPanel} The panel that was removed
42402      */
42403     remove : function(panel, preservePanel)
42404     {
42405         panel = this.getPanel(panel);
42406         if(!panel){
42407             return null;
42408         }
42409         var e = {};
42410         this.fireEvent("beforeremove", this, panel, e);
42411         if(e.cancel === true){
42412             return null;
42413         }
42414         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42415         var panelId = panel.getId();
42416         this.panels.removeKey(panelId);
42417         if(preservePanel){
42418             document.body.appendChild(panel.getEl().dom);
42419         }
42420         if(this.tabs){
42421             this.tabs.removeTab(panel.getEl().id);
42422         }else if (!preservePanel){
42423             this.bodyEl.dom.removeChild(panel.getEl().dom);
42424         }
42425         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42426             var p = this.panels.first();
42427             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42428             tempEl.appendChild(p.getEl().dom);
42429             this.bodyEl.update("");
42430             this.bodyEl.dom.appendChild(p.getEl().dom);
42431             tempEl = null;
42432             this.updateTitle(p.getTitle());
42433             this.tabs = null;
42434             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42435             this.setActivePanel(p);
42436         }
42437         panel.setRegion(null);
42438         if(this.activePanel == panel){
42439             this.activePanel = null;
42440         }
42441         if(this.config.autoDestroy !== false && preservePanel !== true){
42442             try{panel.destroy();}catch(e){}
42443         }
42444         this.fireEvent("panelremoved", this, panel);
42445         return panel;
42446     },
42447
42448     /**
42449      * Returns the TabPanel component used by this region
42450      * @return {Roo.TabPanel}
42451      */
42452     getTabs : function(){
42453         return this.tabs;
42454     },
42455
42456     createTool : function(parentEl, className){
42457         var btn = Roo.DomHelper.append(parentEl, {
42458             tag: "div",
42459             cls: "x-layout-tools-button",
42460             children: [ {
42461                 tag: "div",
42462                 cls: "roo-layout-tools-button-inner " + className,
42463                 html: "&#160;"
42464             }]
42465         }, true);
42466         btn.addClassOnOver("roo-layout-tools-button-over");
42467         return btn;
42468     }
42469 });/*
42470  * Based on:
42471  * Ext JS Library 1.1.1
42472  * Copyright(c) 2006-2007, Ext JS, LLC.
42473  *
42474  * Originally Released Under LGPL - original licence link has changed is not relivant.
42475  *
42476  * Fork - LGPL
42477  * <script type="text/javascript">
42478  */
42479  
42480
42481
42482 /**
42483  * @class Roo.SplitLayoutRegion
42484  * @extends Roo.LayoutRegion
42485  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42486  */
42487 Roo.bootstrap.layout.Split = function(config){
42488     this.cursor = config.cursor;
42489     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42490 };
42491
42492 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42493 {
42494     splitTip : "Drag to resize.",
42495     collapsibleSplitTip : "Drag to resize. Double click to hide.",
42496     useSplitTips : false,
42497
42498     applyConfig : function(config){
42499         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42500     },
42501     
42502     onRender : function(ctr,pos) {
42503         
42504         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42505         if(!this.config.split){
42506             return;
42507         }
42508         if(!this.split){
42509             
42510             var splitEl = Roo.DomHelper.append(ctr.dom,  {
42511                             tag: "div",
42512                             id: this.el.id + "-split",
42513                             cls: "roo-layout-split roo-layout-split-"+this.position,
42514                             html: "&#160;"
42515             });
42516             /** The SplitBar for this region 
42517             * @type Roo.SplitBar */
42518             // does not exist yet...
42519             Roo.log([this.position, this.orientation]);
42520             
42521             this.split = new Roo.bootstrap.SplitBar({
42522                 dragElement : splitEl,
42523                 resizingElement: this.el,
42524                 orientation : this.orientation
42525             });
42526             
42527             this.split.on("moved", this.onSplitMove, this);
42528             this.split.useShim = this.config.useShim === true;
42529             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42530             if(this.useSplitTips){
42531                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42532             }
42533             //if(config.collapsible){
42534             //    this.split.el.on("dblclick", this.collapse,  this);
42535             //}
42536         }
42537         if(typeof this.config.minSize != "undefined"){
42538             this.split.minSize = this.config.minSize;
42539         }
42540         if(typeof this.config.maxSize != "undefined"){
42541             this.split.maxSize = this.config.maxSize;
42542         }
42543         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42544             this.hideSplitter();
42545         }
42546         
42547     },
42548
42549     getHMaxSize : function(){
42550          var cmax = this.config.maxSize || 10000;
42551          var center = this.mgr.getRegion("center");
42552          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42553     },
42554
42555     getVMaxSize : function(){
42556          var cmax = this.config.maxSize || 10000;
42557          var center = this.mgr.getRegion("center");
42558          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42559     },
42560
42561     onSplitMove : function(split, newSize){
42562         this.fireEvent("resized", this, newSize);
42563     },
42564     
42565     /** 
42566      * Returns the {@link Roo.SplitBar} for this region.
42567      * @return {Roo.SplitBar}
42568      */
42569     getSplitBar : function(){
42570         return this.split;
42571     },
42572     
42573     hide : function(){
42574         this.hideSplitter();
42575         Roo.bootstrap.layout.Split.superclass.hide.call(this);
42576     },
42577
42578     hideSplitter : function(){
42579         if(this.split){
42580             this.split.el.setLocation(-2000,-2000);
42581             this.split.el.hide();
42582         }
42583     },
42584
42585     show : function(){
42586         if(this.split){
42587             this.split.el.show();
42588         }
42589         Roo.bootstrap.layout.Split.superclass.show.call(this);
42590     },
42591     
42592     beforeSlide: function(){
42593         if(Roo.isGecko){// firefox overflow auto bug workaround
42594             this.bodyEl.clip();
42595             if(this.tabs) {
42596                 this.tabs.bodyEl.clip();
42597             }
42598             if(this.activePanel){
42599                 this.activePanel.getEl().clip();
42600                 
42601                 if(this.activePanel.beforeSlide){
42602                     this.activePanel.beforeSlide();
42603                 }
42604             }
42605         }
42606     },
42607     
42608     afterSlide : function(){
42609         if(Roo.isGecko){// firefox overflow auto bug workaround
42610             this.bodyEl.unclip();
42611             if(this.tabs) {
42612                 this.tabs.bodyEl.unclip();
42613             }
42614             if(this.activePanel){
42615                 this.activePanel.getEl().unclip();
42616                 if(this.activePanel.afterSlide){
42617                     this.activePanel.afterSlide();
42618                 }
42619             }
42620         }
42621     },
42622
42623     initAutoHide : function(){
42624         if(this.autoHide !== false){
42625             if(!this.autoHideHd){
42626                 var st = new Roo.util.DelayedTask(this.slideIn, this);
42627                 this.autoHideHd = {
42628                     "mouseout": function(e){
42629                         if(!e.within(this.el, true)){
42630                             st.delay(500);
42631                         }
42632                     },
42633                     "mouseover" : function(e){
42634                         st.cancel();
42635                     },
42636                     scope : this
42637                 };
42638             }
42639             this.el.on(this.autoHideHd);
42640         }
42641     },
42642
42643     clearAutoHide : function(){
42644         if(this.autoHide !== false){
42645             this.el.un("mouseout", this.autoHideHd.mouseout);
42646             this.el.un("mouseover", this.autoHideHd.mouseover);
42647         }
42648     },
42649
42650     clearMonitor : function(){
42651         Roo.get(document).un("click", this.slideInIf, this);
42652     },
42653
42654     // these names are backwards but not changed for compat
42655     slideOut : function(){
42656         if(this.isSlid || this.el.hasActiveFx()){
42657             return;
42658         }
42659         this.isSlid = true;
42660         if(this.collapseBtn){
42661             this.collapseBtn.hide();
42662         }
42663         this.closeBtnState = this.closeBtn.getStyle('display');
42664         this.closeBtn.hide();
42665         if(this.stickBtn){
42666             this.stickBtn.show();
42667         }
42668         this.el.show();
42669         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42670         this.beforeSlide();
42671         this.el.setStyle("z-index", 10001);
42672         this.el.slideIn(this.getSlideAnchor(), {
42673             callback: function(){
42674                 this.afterSlide();
42675                 this.initAutoHide();
42676                 Roo.get(document).on("click", this.slideInIf, this);
42677                 this.fireEvent("slideshow", this);
42678             },
42679             scope: this,
42680             block: true
42681         });
42682     },
42683
42684     afterSlideIn : function(){
42685         this.clearAutoHide();
42686         this.isSlid = false;
42687         this.clearMonitor();
42688         this.el.setStyle("z-index", "");
42689         if(this.collapseBtn){
42690             this.collapseBtn.show();
42691         }
42692         this.closeBtn.setStyle('display', this.closeBtnState);
42693         if(this.stickBtn){
42694             this.stickBtn.hide();
42695         }
42696         this.fireEvent("slidehide", this);
42697     },
42698
42699     slideIn : function(cb){
42700         if(!this.isSlid || this.el.hasActiveFx()){
42701             Roo.callback(cb);
42702             return;
42703         }
42704         this.isSlid = false;
42705         this.beforeSlide();
42706         this.el.slideOut(this.getSlideAnchor(), {
42707             callback: function(){
42708                 this.el.setLeftTop(-10000, -10000);
42709                 this.afterSlide();
42710                 this.afterSlideIn();
42711                 Roo.callback(cb);
42712             },
42713             scope: this,
42714             block: true
42715         });
42716     },
42717     
42718     slideInIf : function(e){
42719         if(!e.within(this.el)){
42720             this.slideIn();
42721         }
42722     },
42723
42724     animateCollapse : function(){
42725         this.beforeSlide();
42726         this.el.setStyle("z-index", 20000);
42727         var anchor = this.getSlideAnchor();
42728         this.el.slideOut(anchor, {
42729             callback : function(){
42730                 this.el.setStyle("z-index", "");
42731                 this.collapsedEl.slideIn(anchor, {duration:.3});
42732                 this.afterSlide();
42733                 this.el.setLocation(-10000,-10000);
42734                 this.el.hide();
42735                 this.fireEvent("collapsed", this);
42736             },
42737             scope: this,
42738             block: true
42739         });
42740     },
42741
42742     animateExpand : function(){
42743         this.beforeSlide();
42744         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42745         this.el.setStyle("z-index", 20000);
42746         this.collapsedEl.hide({
42747             duration:.1
42748         });
42749         this.el.slideIn(this.getSlideAnchor(), {
42750             callback : function(){
42751                 this.el.setStyle("z-index", "");
42752                 this.afterSlide();
42753                 if(this.split){
42754                     this.split.el.show();
42755                 }
42756                 this.fireEvent("invalidated", this);
42757                 this.fireEvent("expanded", this);
42758             },
42759             scope: this,
42760             block: true
42761         });
42762     },
42763
42764     anchors : {
42765         "west" : "left",
42766         "east" : "right",
42767         "north" : "top",
42768         "south" : "bottom"
42769     },
42770
42771     sanchors : {
42772         "west" : "l",
42773         "east" : "r",
42774         "north" : "t",
42775         "south" : "b"
42776     },
42777
42778     canchors : {
42779         "west" : "tl-tr",
42780         "east" : "tr-tl",
42781         "north" : "tl-bl",
42782         "south" : "bl-tl"
42783     },
42784
42785     getAnchor : function(){
42786         return this.anchors[this.position];
42787     },
42788
42789     getCollapseAnchor : function(){
42790         return this.canchors[this.position];
42791     },
42792
42793     getSlideAnchor : function(){
42794         return this.sanchors[this.position];
42795     },
42796
42797     getAlignAdj : function(){
42798         var cm = this.cmargins;
42799         switch(this.position){
42800             case "west":
42801                 return [0, 0];
42802             break;
42803             case "east":
42804                 return [0, 0];
42805             break;
42806             case "north":
42807                 return [0, 0];
42808             break;
42809             case "south":
42810                 return [0, 0];
42811             break;
42812         }
42813     },
42814
42815     getExpandAdj : function(){
42816         var c = this.collapsedEl, cm = this.cmargins;
42817         switch(this.position){
42818             case "west":
42819                 return [-(cm.right+c.getWidth()+cm.left), 0];
42820             break;
42821             case "east":
42822                 return [cm.right+c.getWidth()+cm.left, 0];
42823             break;
42824             case "north":
42825                 return [0, -(cm.top+cm.bottom+c.getHeight())];
42826             break;
42827             case "south":
42828                 return [0, cm.top+cm.bottom+c.getHeight()];
42829             break;
42830         }
42831     }
42832 });/*
42833  * Based on:
42834  * Ext JS Library 1.1.1
42835  * Copyright(c) 2006-2007, Ext JS, LLC.
42836  *
42837  * Originally Released Under LGPL - original licence link has changed is not relivant.
42838  *
42839  * Fork - LGPL
42840  * <script type="text/javascript">
42841  */
42842 /*
42843  * These classes are private internal classes
42844  */
42845 Roo.bootstrap.layout.Center = function(config){
42846     config.region = "center";
42847     Roo.bootstrap.layout.Region.call(this, config);
42848     this.visible = true;
42849     this.minWidth = config.minWidth || 20;
42850     this.minHeight = config.minHeight || 20;
42851 };
42852
42853 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42854     hide : function(){
42855         // center panel can't be hidden
42856     },
42857     
42858     show : function(){
42859         // center panel can't be hidden
42860     },
42861     
42862     getMinWidth: function(){
42863         return this.minWidth;
42864     },
42865     
42866     getMinHeight: function(){
42867         return this.minHeight;
42868     }
42869 });
42870
42871
42872
42873
42874  
42875
42876
42877
42878
42879
42880
42881 Roo.bootstrap.layout.North = function(config)
42882 {
42883     config.region = 'north';
42884     config.cursor = 'n-resize';
42885     
42886     Roo.bootstrap.layout.Split.call(this, config);
42887     
42888     
42889     if(this.split){
42890         this.split.placement = Roo.bootstrap.SplitBar.TOP;
42891         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42892         this.split.el.addClass("roo-layout-split-v");
42893     }
42894     //var size = config.initialSize || config.height;
42895     //if(this.el && typeof size != "undefined"){
42896     //    this.el.setHeight(size);
42897     //}
42898 };
42899 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42900 {
42901     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42902      
42903      
42904     onRender : function(ctr, pos)
42905     {
42906         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42907         var size = this.config.initialSize || this.config.height;
42908         if(this.el && typeof size != "undefined"){
42909             this.el.setHeight(size);
42910         }
42911     
42912     },
42913     
42914     getBox : function(){
42915         if(this.collapsed){
42916             return this.collapsedEl.getBox();
42917         }
42918         var box = this.el.getBox();
42919         if(this.split){
42920             box.height += this.split.el.getHeight();
42921         }
42922         return box;
42923     },
42924     
42925     updateBox : function(box){
42926         if(this.split && !this.collapsed){
42927             box.height -= this.split.el.getHeight();
42928             this.split.el.setLeft(box.x);
42929             this.split.el.setTop(box.y+box.height);
42930             this.split.el.setWidth(box.width);
42931         }
42932         if(this.collapsed){
42933             this.updateBody(box.width, null);
42934         }
42935         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42936     }
42937 });
42938
42939
42940
42941
42942
42943 Roo.bootstrap.layout.South = function(config){
42944     config.region = 'south';
42945     config.cursor = 's-resize';
42946     Roo.bootstrap.layout.Split.call(this, config);
42947     if(this.split){
42948         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42949         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42950         this.split.el.addClass("roo-layout-split-v");
42951     }
42952     
42953 };
42954
42955 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42956     orientation: Roo.bootstrap.SplitBar.VERTICAL,
42957     
42958     onRender : function(ctr, pos)
42959     {
42960         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42961         var size = this.config.initialSize || this.config.height;
42962         if(this.el && typeof size != "undefined"){
42963             this.el.setHeight(size);
42964         }
42965     
42966     },
42967     
42968     getBox : function(){
42969         if(this.collapsed){
42970             return this.collapsedEl.getBox();
42971         }
42972         var box = this.el.getBox();
42973         if(this.split){
42974             var sh = this.split.el.getHeight();
42975             box.height += sh;
42976             box.y -= sh;
42977         }
42978         return box;
42979     },
42980     
42981     updateBox : function(box){
42982         if(this.split && !this.collapsed){
42983             var sh = this.split.el.getHeight();
42984             box.height -= sh;
42985             box.y += sh;
42986             this.split.el.setLeft(box.x);
42987             this.split.el.setTop(box.y-sh);
42988             this.split.el.setWidth(box.width);
42989         }
42990         if(this.collapsed){
42991             this.updateBody(box.width, null);
42992         }
42993         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42994     }
42995 });
42996
42997 Roo.bootstrap.layout.East = function(config){
42998     config.region = "east";
42999     config.cursor = "e-resize";
43000     Roo.bootstrap.layout.Split.call(this, config);
43001     if(this.split){
43002         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
43003         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43004         this.split.el.addClass("roo-layout-split-h");
43005     }
43006     
43007 };
43008 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
43009     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43010     
43011     onRender : function(ctr, pos)
43012     {
43013         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
43014         var size = this.config.initialSize || this.config.width;
43015         if(this.el && typeof size != "undefined"){
43016             this.el.setWidth(size);
43017         }
43018     
43019     },
43020     
43021     getBox : function(){
43022         if(this.collapsed){
43023             return this.collapsedEl.getBox();
43024         }
43025         var box = this.el.getBox();
43026         if(this.split){
43027             var sw = this.split.el.getWidth();
43028             box.width += sw;
43029             box.x -= sw;
43030         }
43031         return box;
43032     },
43033
43034     updateBox : function(box){
43035         if(this.split && !this.collapsed){
43036             var sw = this.split.el.getWidth();
43037             box.width -= sw;
43038             this.split.el.setLeft(box.x);
43039             this.split.el.setTop(box.y);
43040             this.split.el.setHeight(box.height);
43041             box.x += sw;
43042         }
43043         if(this.collapsed){
43044             this.updateBody(null, box.height);
43045         }
43046         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43047     }
43048 });
43049
43050 Roo.bootstrap.layout.West = function(config){
43051     config.region = "west";
43052     config.cursor = "w-resize";
43053     
43054     Roo.bootstrap.layout.Split.call(this, config);
43055     if(this.split){
43056         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
43057         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
43058         this.split.el.addClass("roo-layout-split-h");
43059     }
43060     
43061 };
43062 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
43063     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
43064     
43065     onRender: function(ctr, pos)
43066     {
43067         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
43068         var size = this.config.initialSize || this.config.width;
43069         if(typeof size != "undefined"){
43070             this.el.setWidth(size);
43071         }
43072     },
43073     
43074     getBox : function(){
43075         if(this.collapsed){
43076             return this.collapsedEl.getBox();
43077         }
43078         var box = this.el.getBox();
43079         if (box.width == 0) {
43080             box.width = this.config.width; // kludge?
43081         }
43082         if(this.split){
43083             box.width += this.split.el.getWidth();
43084         }
43085         return box;
43086     },
43087     
43088     updateBox : function(box){
43089         if(this.split && !this.collapsed){
43090             var sw = this.split.el.getWidth();
43091             box.width -= sw;
43092             this.split.el.setLeft(box.x+box.width);
43093             this.split.el.setTop(box.y);
43094             this.split.el.setHeight(box.height);
43095         }
43096         if(this.collapsed){
43097             this.updateBody(null, box.height);
43098         }
43099         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
43100     }
43101 });/*
43102  * Based on:
43103  * Ext JS Library 1.1.1
43104  * Copyright(c) 2006-2007, Ext JS, LLC.
43105  *
43106  * Originally Released Under LGPL - original licence link has changed is not relivant.
43107  *
43108  * Fork - LGPL
43109  * <script type="text/javascript">
43110  */
43111 /**
43112  * @class Roo.bootstrap.paenl.Content
43113  * @extends Roo.util.Observable
43114  * @children Roo.bootstrap.Component
43115  * @parent builder Roo.bootstrap.layout.Border
43116  * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
43117  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
43118  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
43119  * @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
43120  * @cfg {Boolean}   closable      True if the panel can be closed/removed
43121  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
43122  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
43123  * @cfg {Toolbar}   toolbar       A toolbar for this panel
43124  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
43125  * @cfg {String} title          The title for this panel
43126  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
43127  * @cfg {String} url            Calls {@link #setUrl} with this value
43128  * @cfg {String} region  [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
43129  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
43130  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
43131  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
43132  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
43133  * @cfg {Boolean} badges render the badges
43134  * @cfg {String} cls  extra classes to use  
43135  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
43136  
43137  * @constructor
43138  * Create a new ContentPanel.
43139  * @param {String/Object} config A string to set only the title or a config object
43140  
43141  */
43142 Roo.bootstrap.panel.Content = function( config){
43143     
43144     this.tpl = config.tpl || false;
43145     
43146     var el = config.el;
43147     var content = config.content;
43148
43149     if(config.autoCreate){ // xtype is available if this is called from factory
43150         el = Roo.id();
43151     }
43152     this.el = Roo.get(el);
43153     if(!this.el && config && config.autoCreate){
43154         if(typeof config.autoCreate == "object"){
43155             if(!config.autoCreate.id){
43156                 config.autoCreate.id = config.id||el;
43157             }
43158             this.el = Roo.DomHelper.append(document.body,
43159                         config.autoCreate, true);
43160         }else{
43161             var elcfg =  {
43162                 tag: "div",
43163                 cls: (config.cls || '') +
43164                     (config.background ? ' bg-' + config.background : '') +
43165                     " roo-layout-inactive-content",
43166                 id: config.id||el
43167             };
43168             if (config.iframe) {
43169                 elcfg.cn = [
43170                     {
43171                         tag : 'iframe',
43172                         style : 'border: 0px',
43173                         src : 'about:blank'
43174                     }
43175                 ];
43176             }
43177               
43178             if (config.html) {
43179                 elcfg.html = config.html;
43180                 
43181             }
43182                         
43183             this.el = Roo.DomHelper.append(document.body, elcfg , true);
43184             if (config.iframe) {
43185                 this.iframeEl = this.el.select('iframe',true).first();
43186             }
43187             
43188         }
43189     } 
43190     this.closable = false;
43191     this.loaded = false;
43192     this.active = false;
43193    
43194       
43195     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
43196         
43197         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43198         
43199         this.wrapEl = this.el; //this.el.wrap();
43200         var ti = [];
43201         if (config.toolbar.items) {
43202             ti = config.toolbar.items ;
43203             delete config.toolbar.items ;
43204         }
43205         
43206         var nitems = [];
43207         this.toolbar.render(this.wrapEl, 'before');
43208         for(var i =0;i < ti.length;i++) {
43209           //  Roo.log(['add child', items[i]]);
43210             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43211         }
43212         this.toolbar.items = nitems;
43213         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43214         delete config.toolbar;
43215         
43216     }
43217     /*
43218     // xtype created footer. - not sure if will work as we normally have to render first..
43219     if (this.footer && !this.footer.el && this.footer.xtype) {
43220         if (!this.wrapEl) {
43221             this.wrapEl = this.el.wrap();
43222         }
43223     
43224         this.footer.container = this.wrapEl.createChild();
43225          
43226         this.footer = Roo.factory(this.footer, Roo);
43227         
43228     }
43229     */
43230     
43231      if(typeof config == "string"){
43232         this.title = config;
43233     }else{
43234         Roo.apply(this, config);
43235     }
43236     
43237     if(this.resizeEl){
43238         this.resizeEl = Roo.get(this.resizeEl, true);
43239     }else{
43240         this.resizeEl = this.el;
43241     }
43242     // handle view.xtype
43243     
43244  
43245     
43246     
43247     this.addEvents({
43248         /**
43249          * @event activate
43250          * Fires when this panel is activated. 
43251          * @param {Roo.ContentPanel} this
43252          */
43253         "activate" : true,
43254         /**
43255          * @event deactivate
43256          * Fires when this panel is activated. 
43257          * @param {Roo.ContentPanel} this
43258          */
43259         "deactivate" : true,
43260
43261         /**
43262          * @event resize
43263          * Fires when this panel is resized if fitToFrame is true.
43264          * @param {Roo.ContentPanel} this
43265          * @param {Number} width The width after any component adjustments
43266          * @param {Number} height The height after any component adjustments
43267          */
43268         "resize" : true,
43269         
43270          /**
43271          * @event render
43272          * Fires when this tab is created
43273          * @param {Roo.ContentPanel} this
43274          */
43275         "render" : true,
43276         
43277           /**
43278          * @event scroll
43279          * Fires when this content is scrolled
43280          * @param {Roo.ContentPanel} this
43281          * @param {Event} scrollEvent
43282          */
43283         "scroll" : true
43284         
43285         
43286         
43287     });
43288     
43289
43290     
43291     
43292     if(this.autoScroll && !this.iframe){
43293         this.resizeEl.setStyle("overflow", "auto");
43294         this.resizeEl.on('scroll', this.onScroll, this);
43295     } else {
43296         // fix randome scrolling
43297         //this.el.on('scroll', function() {
43298         //    Roo.log('fix random scolling');
43299         //    this.scrollTo('top',0); 
43300         //});
43301     }
43302     content = content || this.content;
43303     if(content){
43304         this.setContent(content);
43305     }
43306     if(config && config.url){
43307         this.setUrl(this.url, this.params, this.loadOnce);
43308     }
43309     
43310     
43311     
43312     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43313     
43314     if (this.view && typeof(this.view.xtype) != 'undefined') {
43315         this.view.el = this.el.appendChild(document.createElement("div"));
43316         this.view = Roo.factory(this.view); 
43317         this.view.render  &&  this.view.render(false, '');  
43318     }
43319     
43320     
43321     this.fireEvent('render', this);
43322 };
43323
43324 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43325     
43326     cls : '',
43327     background : '',
43328     
43329     tabTip : '',
43330     
43331     iframe : false,
43332     iframeEl : false,
43333     
43334     /* Resize Element - use this to work out scroll etc. */
43335     resizeEl : false,
43336     
43337     setRegion : function(region){
43338         this.region = region;
43339         this.setActiveClass(region && !this.background);
43340     },
43341     
43342     
43343     setActiveClass: function(state)
43344     {
43345         if(state){
43346            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43347            this.el.setStyle('position','relative');
43348         }else{
43349            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43350            this.el.setStyle('position', 'absolute');
43351         } 
43352     },
43353     
43354     /**
43355      * Returns the toolbar for this Panel if one was configured. 
43356      * @return {Roo.Toolbar} 
43357      */
43358     getToolbar : function(){
43359         return this.toolbar;
43360     },
43361     
43362     setActiveState : function(active)
43363     {
43364         this.active = active;
43365         this.setActiveClass(active);
43366         if(!active){
43367             if(this.fireEvent("deactivate", this) === false){
43368                 return false;
43369             }
43370             return true;
43371         }
43372         this.fireEvent("activate", this);
43373         return true;
43374     },
43375     /**
43376      * Updates this panel's element (not for iframe)
43377      * @param {String} content The new content
43378      * @param {Boolean} loadScripts (optional) true to look for and process scripts
43379     */
43380     setContent : function(content, loadScripts){
43381         if (this.iframe) {
43382             return;
43383         }
43384         
43385         this.el.update(content, loadScripts);
43386     },
43387
43388     ignoreResize : function(w, h)
43389     {
43390         //return false; // always resize?
43391         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43392             return true;
43393         }else{
43394             this.lastSize = {width: w, height: h};
43395             return false;
43396         }
43397     },
43398     /**
43399      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43400      * @return {Roo.UpdateManager} The UpdateManager
43401      */
43402     getUpdateManager : function(){
43403         if (this.iframe) {
43404             return false;
43405         }
43406         return this.el.getUpdateManager();
43407     },
43408      /**
43409      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43410      * Does not work with IFRAME contents
43411      * @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:
43412 <pre><code>
43413 panel.load({
43414     url: "your-url.php",
43415     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43416     callback: yourFunction,
43417     scope: yourObject, //(optional scope)
43418     discardUrl: false,
43419     nocache: false,
43420     text: "Loading...",
43421     timeout: 30,
43422     scripts: false
43423 });
43424 </code></pre>
43425      
43426      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43427      * 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.
43428      * @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}
43429      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43430      * @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.
43431      * @return {Roo.ContentPanel} this
43432      */
43433     load : function(){
43434         
43435         if (this.iframe) {
43436             return this;
43437         }
43438         
43439         var um = this.el.getUpdateManager();
43440         um.update.apply(um, arguments);
43441         return this;
43442     },
43443
43444
43445     /**
43446      * 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.
43447      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43448      * @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)
43449      * @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)
43450      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43451      */
43452     setUrl : function(url, params, loadOnce){
43453         if (this.iframe) {
43454             this.iframeEl.dom.src = url;
43455             return false;
43456         }
43457         
43458         if(this.refreshDelegate){
43459             this.removeListener("activate", this.refreshDelegate);
43460         }
43461         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43462         this.on("activate", this.refreshDelegate);
43463         return this.el.getUpdateManager();
43464     },
43465     
43466     _handleRefresh : function(url, params, loadOnce){
43467         if(!loadOnce || !this.loaded){
43468             var updater = this.el.getUpdateManager();
43469             updater.update(url, params, this._setLoaded.createDelegate(this));
43470         }
43471     },
43472     
43473     _setLoaded : function(){
43474         this.loaded = true;
43475     }, 
43476     
43477     /**
43478      * Returns this panel's id
43479      * @return {String} 
43480      */
43481     getId : function(){
43482         return this.el.id;
43483     },
43484     
43485     /** 
43486      * Returns this panel's element - used by regiosn to add.
43487      * @return {Roo.Element} 
43488      */
43489     getEl : function(){
43490         return this.wrapEl || this.el;
43491     },
43492     
43493    
43494     
43495     adjustForComponents : function(width, height)
43496     {
43497         //Roo.log('adjustForComponents ');
43498         if(this.resizeEl != this.el){
43499             width -= this.el.getFrameWidth('lr');
43500             height -= this.el.getFrameWidth('tb');
43501         }
43502         if(this.toolbar){
43503             var te = this.toolbar.getEl();
43504             te.setWidth(width);
43505             height -= te.getHeight();
43506         }
43507         if(this.footer){
43508             var te = this.footer.getEl();
43509             te.setWidth(width);
43510             height -= te.getHeight();
43511         }
43512         
43513         
43514         if(this.adjustments){
43515             width += this.adjustments[0];
43516             height += this.adjustments[1];
43517         }
43518         return {"width": width, "height": height};
43519     },
43520     
43521     setSize : function(width, height){
43522         if(this.fitToFrame && !this.ignoreResize(width, height)){
43523             if(this.fitContainer && this.resizeEl != this.el){
43524                 this.el.setSize(width, height);
43525             }
43526             var size = this.adjustForComponents(width, height);
43527             if (this.iframe) {
43528                 this.iframeEl.setSize(width,height);
43529             }
43530             
43531             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43532             this.fireEvent('resize', this, size.width, size.height);
43533             
43534             
43535         }
43536     },
43537     
43538     /**
43539      * Returns this panel's title
43540      * @return {String} 
43541      */
43542     getTitle : function(){
43543         
43544         if (typeof(this.title) != 'object') {
43545             return this.title;
43546         }
43547         
43548         var t = '';
43549         for (var k in this.title) {
43550             if (!this.title.hasOwnProperty(k)) {
43551                 continue;
43552             }
43553             
43554             if (k.indexOf('-') >= 0) {
43555                 var s = k.split('-');
43556                 for (var i = 0; i<s.length; i++) {
43557                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43558                 }
43559             } else {
43560                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43561             }
43562         }
43563         return t;
43564     },
43565     
43566     /**
43567      * Set this panel's title
43568      * @param {String} title
43569      */
43570     setTitle : function(title){
43571         this.title = title;
43572         if(this.region){
43573             this.region.updatePanelTitle(this, title);
43574         }
43575     },
43576     
43577     /**
43578      * Returns true is this panel was configured to be closable
43579      * @return {Boolean} 
43580      */
43581     isClosable : function(){
43582         return this.closable;
43583     },
43584     
43585     beforeSlide : function(){
43586         this.el.clip();
43587         this.resizeEl.clip();
43588     },
43589     
43590     afterSlide : function(){
43591         this.el.unclip();
43592         this.resizeEl.unclip();
43593     },
43594     
43595     /**
43596      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
43597      *   Will fail silently if the {@link #setUrl} method has not been called.
43598      *   This does not activate the panel, just updates its content.
43599      */
43600     refresh : function(){
43601         if(this.refreshDelegate){
43602            this.loaded = false;
43603            this.refreshDelegate();
43604         }
43605     },
43606     
43607     /**
43608      * Destroys this panel
43609      */
43610     destroy : function(){
43611         this.el.removeAllListeners();
43612         var tempEl = document.createElement("span");
43613         tempEl.appendChild(this.el.dom);
43614         tempEl.innerHTML = "";
43615         this.el.remove();
43616         this.el = null;
43617     },
43618     
43619     /**
43620      * form - if the content panel contains a form - this is a reference to it.
43621      * @type {Roo.form.Form}
43622      */
43623     form : false,
43624     /**
43625      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43626      *    This contains a reference to it.
43627      * @type {Roo.View}
43628      */
43629     view : false,
43630     
43631       /**
43632      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43633      * <pre><code>
43634
43635 layout.addxtype({
43636        xtype : 'Form',
43637        items: [ .... ]
43638    }
43639 );
43640
43641 </code></pre>
43642      * @param {Object} cfg Xtype definition of item to add.
43643      */
43644     
43645     
43646     getChildContainer: function () {
43647         return this.getEl();
43648     },
43649     
43650     
43651     onScroll : function(e)
43652     {
43653         this.fireEvent('scroll', this, e);
43654     }
43655     
43656     
43657     /*
43658         var  ret = new Roo.factory(cfg);
43659         return ret;
43660         
43661         
43662         // add form..
43663         if (cfg.xtype.match(/^Form$/)) {
43664             
43665             var el;
43666             //if (this.footer) {
43667             //    el = this.footer.container.insertSibling(false, 'before');
43668             //} else {
43669                 el = this.el.createChild();
43670             //}
43671
43672             this.form = new  Roo.form.Form(cfg);
43673             
43674             
43675             if ( this.form.allItems.length) {
43676                 this.form.render(el.dom);
43677             }
43678             return this.form;
43679         }
43680         // should only have one of theses..
43681         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43682             // views.. should not be just added - used named prop 'view''
43683             
43684             cfg.el = this.el.appendChild(document.createElement("div"));
43685             // factory?
43686             
43687             var ret = new Roo.factory(cfg);
43688              
43689              ret.render && ret.render(false, ''); // render blank..
43690             this.view = ret;
43691             return ret;
43692         }
43693         return false;
43694     }
43695     \*/
43696 });
43697  
43698 /**
43699  * @class Roo.bootstrap.panel.Grid
43700  * @extends Roo.bootstrap.panel.Content
43701  * @constructor
43702  * Create a new GridPanel.
43703  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43704  * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43705  * @param {Object} config A the config object
43706   
43707  */
43708
43709
43710
43711 Roo.bootstrap.panel.Grid = function(config)
43712 {
43713     
43714       
43715     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43716         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43717
43718     config.el = this.wrapper;
43719     //this.el = this.wrapper;
43720     
43721       if (config.container) {
43722         // ctor'ed from a Border/panel.grid
43723         
43724         
43725         this.wrapper.setStyle("overflow", "hidden");
43726         this.wrapper.addClass('roo-grid-container');
43727
43728     }
43729     
43730     
43731     if(config.toolbar){
43732         var tool_el = this.wrapper.createChild();    
43733         this.toolbar = Roo.factory(config.toolbar);
43734         var ti = [];
43735         if (config.toolbar.items) {
43736             ti = config.toolbar.items ;
43737             delete config.toolbar.items ;
43738         }
43739         
43740         var nitems = [];
43741         this.toolbar.render(tool_el);
43742         for(var i =0;i < ti.length;i++) {
43743           //  Roo.log(['add child', items[i]]);
43744             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43745         }
43746         this.toolbar.items = nitems;
43747         
43748         delete config.toolbar;
43749     }
43750     
43751     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43752     config.grid.scrollBody = true;;
43753     config.grid.monitorWindowResize = false; // turn off autosizing
43754     config.grid.autoHeight = false;
43755     config.grid.autoWidth = false;
43756     
43757     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43758     
43759     if (config.background) {
43760         // render grid on panel activation (if panel background)
43761         this.on('activate', function(gp) {
43762             if (!gp.grid.rendered) {
43763                 gp.grid.render(this.wrapper);
43764                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
43765             }
43766         });
43767             
43768     } else {
43769         this.grid.render(this.wrapper);
43770         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
43771
43772     }
43773     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43774     // ??? needed ??? config.el = this.wrapper;
43775     
43776     
43777     
43778   
43779     // xtype created footer. - not sure if will work as we normally have to render first..
43780     if (this.footer && !this.footer.el && this.footer.xtype) {
43781         
43782         var ctr = this.grid.getView().getFooterPanel(true);
43783         this.footer.dataSource = this.grid.dataSource;
43784         this.footer = Roo.factory(this.footer, Roo);
43785         this.footer.render(ctr);
43786         
43787     }
43788     
43789     
43790     
43791     
43792      
43793 };
43794
43795 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43796 {
43797   
43798     getId : function(){
43799         return this.grid.id;
43800     },
43801     
43802     /**
43803      * Returns the grid for this panel
43804      * @return {Roo.bootstrap.Table} 
43805      */
43806     getGrid : function(){
43807         return this.grid;    
43808     },
43809     
43810     setSize : function(width, height)
43811     {
43812      
43813         //if(!this.ignoreResize(width, height)){
43814             var grid = this.grid;
43815             var size = this.adjustForComponents(width, height);
43816             // tfoot is not a footer?
43817           
43818             
43819             var gridel = grid.getGridEl();
43820             gridel.setSize(size.width, size.height);
43821             
43822             var tbd = grid.getGridEl().select('tbody', true).first();
43823             var thd = grid.getGridEl().select('thead',true).first();
43824             var tbf= grid.getGridEl().select('tfoot', true).first();
43825
43826             if (tbf) {
43827                 size.height -= tbf.getHeight();
43828             }
43829             if (thd) {
43830                 size.height -= thd.getHeight();
43831             }
43832             
43833             tbd.setSize(size.width, size.height );
43834             // this is for the account management tab -seems to work there.
43835             var thd = grid.getGridEl().select('thead',true).first();
43836             //if (tbd) {
43837             //    tbd.setSize(size.width, size.height - thd.getHeight());
43838             //}
43839              
43840             grid.autoSize();
43841         //}
43842    
43843     },
43844      
43845     
43846     
43847     beforeSlide : function(){
43848         this.grid.getView().scroller.clip();
43849     },
43850     
43851     afterSlide : function(){
43852         this.grid.getView().scroller.unclip();
43853     },
43854     
43855     destroy : function(){
43856         this.grid.destroy();
43857         delete this.grid;
43858         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
43859     }
43860 });
43861
43862 /**
43863  * @class Roo.bootstrap.panel.Nest
43864  * @extends Roo.bootstrap.panel.Content
43865  * @constructor
43866  * Create a new Panel, that can contain a layout.Border.
43867  * 
43868  * 
43869  * @param {String/Object} config A string to set only the title or a config object
43870  */
43871 Roo.bootstrap.panel.Nest = function(config)
43872 {
43873     // construct with only one argument..
43874     /* FIXME - implement nicer consturctors
43875     if (layout.layout) {
43876         config = layout;
43877         layout = config.layout;
43878         delete config.layout;
43879     }
43880     if (layout.xtype && !layout.getEl) {
43881         // then layout needs constructing..
43882         layout = Roo.factory(layout, Roo);
43883     }
43884     */
43885     
43886     config.el =  config.layout.getEl();
43887     
43888     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43889     
43890     config.layout.monitorWindowResize = false; // turn off autosizing
43891     this.layout = config.layout;
43892     this.layout.getEl().addClass("roo-layout-nested-layout");
43893     this.layout.parent = this;
43894     
43895     
43896     
43897     
43898 };
43899
43900 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43901     /**
43902     * @cfg {Roo.BorderLayout} layout The layout for this panel
43903     */
43904     layout : false,
43905
43906     setSize : function(width, height){
43907         if(!this.ignoreResize(width, height)){
43908             var size = this.adjustForComponents(width, height);
43909             var el = this.layout.getEl();
43910             if (size.height < 1) {
43911                 el.setWidth(size.width);   
43912             } else {
43913                 el.setSize(size.width, size.height);
43914             }
43915             var touch = el.dom.offsetWidth;
43916             this.layout.layout();
43917             // ie requires a double layout on the first pass
43918             if(Roo.isIE && !this.initialized){
43919                 this.initialized = true;
43920                 this.layout.layout();
43921             }
43922         }
43923     },
43924     
43925     // activate all subpanels if not currently active..
43926     
43927     setActiveState : function(active){
43928         this.active = active;
43929         this.setActiveClass(active);
43930         
43931         if(!active){
43932             this.fireEvent("deactivate", this);
43933             return;
43934         }
43935         
43936         this.fireEvent("activate", this);
43937         // not sure if this should happen before or after..
43938         if (!this.layout) {
43939             return; // should not happen..
43940         }
43941         var reg = false;
43942         for (var r in this.layout.regions) {
43943             reg = this.layout.getRegion(r);
43944             if (reg.getActivePanel()) {
43945                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
43946                 reg.setActivePanel(reg.getActivePanel());
43947                 continue;
43948             }
43949             if (!reg.panels.length) {
43950                 continue;
43951             }
43952             reg.showPanel(reg.getPanel(0));
43953         }
43954         
43955         
43956         
43957         
43958     },
43959     
43960     /**
43961      * Returns the nested BorderLayout for this panel
43962      * @return {Roo.BorderLayout} 
43963      */
43964     getLayout : function(){
43965         return this.layout;
43966     },
43967     
43968      /**
43969      * Adds a xtype elements to the layout of the nested panel
43970      * <pre><code>
43971
43972 panel.addxtype({
43973        xtype : 'ContentPanel',
43974        region: 'west',
43975        items: [ .... ]
43976    }
43977 );
43978
43979 panel.addxtype({
43980         xtype : 'NestedLayoutPanel',
43981         region: 'west',
43982         layout: {
43983            center: { },
43984            west: { }   
43985         },
43986         items : [ ... list of content panels or nested layout panels.. ]
43987    }
43988 );
43989 </code></pre>
43990      * @param {Object} cfg Xtype definition of item to add.
43991      */
43992     addxtype : function(cfg) {
43993         return this.layout.addxtype(cfg);
43994     
43995     }
43996 });/*
43997  * Based on:
43998  * Ext JS Library 1.1.1
43999  * Copyright(c) 2006-2007, Ext JS, LLC.
44000  *
44001  * Originally Released Under LGPL - original licence link has changed is not relivant.
44002  *
44003  * Fork - LGPL
44004  * <script type="text/javascript">
44005  */
44006 /**
44007  * @class Roo.TabPanel
44008  * @extends Roo.util.Observable
44009  * A lightweight tab container.
44010  * <br><br>
44011  * Usage:
44012  * <pre><code>
44013 // basic tabs 1, built from existing content
44014 var tabs = new Roo.TabPanel("tabs1");
44015 tabs.addTab("script", "View Script");
44016 tabs.addTab("markup", "View Markup");
44017 tabs.activate("script");
44018
44019 // more advanced tabs, built from javascript
44020 var jtabs = new Roo.TabPanel("jtabs");
44021 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
44022
44023 // set up the UpdateManager
44024 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
44025 var updater = tab2.getUpdateManager();
44026 updater.setDefaultUrl("ajax1.htm");
44027 tab2.on('activate', updater.refresh, updater, true);
44028
44029 // Use setUrl for Ajax loading
44030 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
44031 tab3.setUrl("ajax2.htm", null, true);
44032
44033 // Disabled tab
44034 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
44035 tab4.disable();
44036
44037 jtabs.activate("jtabs-1");
44038  * </code></pre>
44039  * @constructor
44040  * Create a new TabPanel.
44041  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
44042  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
44043  */
44044 Roo.bootstrap.panel.Tabs = function(config){
44045     /**
44046     * The container element for this TabPanel.
44047     * @type Roo.Element
44048     */
44049     this.el = Roo.get(config.el);
44050     delete config.el;
44051     if(config){
44052         if(typeof config == "boolean"){
44053             this.tabPosition = config ? "bottom" : "top";
44054         }else{
44055             Roo.apply(this, config);
44056         }
44057     }
44058     
44059     if(this.tabPosition == "bottom"){
44060         // if tabs are at the bottom = create the body first.
44061         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44062         this.el.addClass("roo-tabs-bottom");
44063     }
44064     // next create the tabs holders
44065     
44066     if (this.tabPosition == "west"){
44067         
44068         var reg = this.region; // fake it..
44069         while (reg) {
44070             if (!reg.mgr.parent) {
44071                 break;
44072             }
44073             reg = reg.mgr.parent.region;
44074         }
44075         Roo.log("got nest?");
44076         Roo.log(reg);
44077         if (reg.mgr.getRegion('west')) {
44078             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
44079             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
44080             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44081             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44082             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44083         
44084             
44085         }
44086         
44087         
44088     } else {
44089      
44090         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
44091         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
44092         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
44093         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
44094     }
44095     
44096     
44097     if(Roo.isIE){
44098         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
44099     }
44100     
44101     // finally - if tabs are at the top, then create the body last..
44102     if(this.tabPosition != "bottom"){
44103         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
44104          * @type Roo.Element
44105          */
44106         this.bodyEl = Roo.get(this.createBody(this.el.dom));
44107         this.el.addClass("roo-tabs-top");
44108     }
44109     this.items = [];
44110
44111     this.bodyEl.setStyle("position", "relative");
44112
44113     this.active = null;
44114     this.activateDelegate = this.activate.createDelegate(this);
44115
44116     this.addEvents({
44117         /**
44118          * @event tabchange
44119          * Fires when the active tab changes
44120          * @param {Roo.TabPanel} this
44121          * @param {Roo.TabPanelItem} activePanel The new active tab
44122          */
44123         "tabchange": true,
44124         /**
44125          * @event beforetabchange
44126          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
44127          * @param {Roo.TabPanel} this
44128          * @param {Object} e Set cancel to true on this object to cancel the tab change
44129          * @param {Roo.TabPanelItem} tab The tab being changed to
44130          */
44131         "beforetabchange" : true
44132     });
44133
44134     Roo.EventManager.onWindowResize(this.onResize, this);
44135     this.cpad = this.el.getPadding("lr");
44136     this.hiddenCount = 0;
44137
44138
44139     // toolbar on the tabbar support...
44140     if (this.toolbar) {
44141         alert("no toolbar support yet");
44142         this.toolbar  = false;
44143         /*
44144         var tcfg = this.toolbar;
44145         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
44146         this.toolbar = new Roo.Toolbar(tcfg);
44147         if (Roo.isSafari) {
44148             var tbl = tcfg.container.child('table', true);
44149             tbl.setAttribute('width', '100%');
44150         }
44151         */
44152         
44153     }
44154    
44155
44156
44157     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
44158 };
44159
44160 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
44161     /*
44162      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
44163      */
44164     tabPosition : "top",
44165     /*
44166      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
44167      */
44168     currentTabWidth : 0,
44169     /*
44170      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
44171      */
44172     minTabWidth : 40,
44173     /*
44174      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
44175      */
44176     maxTabWidth : 250,
44177     /*
44178      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
44179      */
44180     preferredTabWidth : 175,
44181     /*
44182      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
44183      */
44184     resizeTabs : false,
44185     /*
44186      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
44187      */
44188     monitorResize : true,
44189     /*
44190      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
44191      */
44192     toolbar : false,  // set by caller..
44193     
44194     region : false, /// set by caller
44195     
44196     disableTooltips : true, // not used yet...
44197
44198     /**
44199      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44200      * @param {String} id The id of the div to use <b>or create</b>
44201      * @param {String} text The text for the tab
44202      * @param {String} content (optional) Content to put in the TabPanelItem body
44203      * @param {Boolean} closable (optional) True to create a close icon on the tab
44204      * @return {Roo.TabPanelItem} The created TabPanelItem
44205      */
44206     addTab : function(id, text, content, closable, tpl)
44207     {
44208         var item = new Roo.bootstrap.panel.TabItem({
44209             panel: this,
44210             id : id,
44211             text : text,
44212             closable : closable,
44213             tpl : tpl
44214         });
44215         this.addTabItem(item);
44216         if(content){
44217             item.setContent(content);
44218         }
44219         return item;
44220     },
44221
44222     /**
44223      * Returns the {@link Roo.TabPanelItem} with the specified id/index
44224      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44225      * @return {Roo.TabPanelItem}
44226      */
44227     getTab : function(id){
44228         return this.items[id];
44229     },
44230
44231     /**
44232      * Hides the {@link Roo.TabPanelItem} with the specified id/index
44233      * @param {String/Number} id The id or index of the TabPanelItem to hide.
44234      */
44235     hideTab : function(id){
44236         var t = this.items[id];
44237         if(!t.isHidden()){
44238            t.setHidden(true);
44239            this.hiddenCount++;
44240            this.autoSizeTabs();
44241         }
44242     },
44243
44244     /**
44245      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44246      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44247      */
44248     unhideTab : function(id){
44249         var t = this.items[id];
44250         if(t.isHidden()){
44251            t.setHidden(false);
44252            this.hiddenCount--;
44253            this.autoSizeTabs();
44254         }
44255     },
44256
44257     /**
44258      * Adds an existing {@link Roo.TabPanelItem}.
44259      * @param {Roo.TabPanelItem} item The TabPanelItem to add
44260      */
44261     addTabItem : function(item)
44262     {
44263         this.items[item.id] = item;
44264         this.items.push(item);
44265         this.autoSizeTabs();
44266       //  if(this.resizeTabs){
44267     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44268   //         this.autoSizeTabs();
44269 //        }else{
44270 //            item.autoSize();
44271        // }
44272     },
44273
44274     /**
44275      * Removes a {@link Roo.TabPanelItem}.
44276      * @param {String/Number} id The id or index of the TabPanelItem to remove.
44277      */
44278     removeTab : function(id){
44279         var items = this.items;
44280         var tab = items[id];
44281         if(!tab) { return; }
44282         var index = items.indexOf(tab);
44283         if(this.active == tab && items.length > 1){
44284             var newTab = this.getNextAvailable(index);
44285             if(newTab) {
44286                 newTab.activate();
44287             }
44288         }
44289         this.stripEl.dom.removeChild(tab.pnode.dom);
44290         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44291             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44292         }
44293         items.splice(index, 1);
44294         delete this.items[tab.id];
44295         tab.fireEvent("close", tab);
44296         tab.purgeListeners();
44297         this.autoSizeTabs();
44298     },
44299
44300     getNextAvailable : function(start){
44301         var items = this.items;
44302         var index = start;
44303         // look for a next tab that will slide over to
44304         // replace the one being removed
44305         while(index < items.length){
44306             var item = items[++index];
44307             if(item && !item.isHidden()){
44308                 return item;
44309             }
44310         }
44311         // if one isn't found select the previous tab (on the left)
44312         index = start;
44313         while(index >= 0){
44314             var item = items[--index];
44315             if(item && !item.isHidden()){
44316                 return item;
44317             }
44318         }
44319         return null;
44320     },
44321
44322     /**
44323      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44324      * @param {String/Number} id The id or index of the TabPanelItem to disable.
44325      */
44326     disableTab : function(id){
44327         var tab = this.items[id];
44328         if(tab && this.active != tab){
44329             tab.disable();
44330         }
44331     },
44332
44333     /**
44334      * Enables a {@link Roo.TabPanelItem} that is disabled.
44335      * @param {String/Number} id The id or index of the TabPanelItem to enable.
44336      */
44337     enableTab : function(id){
44338         var tab = this.items[id];
44339         tab.enable();
44340     },
44341
44342     /**
44343      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44344      * @param {String/Number} id The id or index of the TabPanelItem to activate.
44345      * @return {Roo.TabPanelItem} The TabPanelItem.
44346      */
44347     activate : function(id)
44348     {
44349         //Roo.log('activite:'  + id);
44350         
44351         var tab = this.items[id];
44352         if(!tab){
44353             return null;
44354         }
44355         if(tab == this.active || tab.disabled){
44356             return tab;
44357         }
44358         var e = {};
44359         this.fireEvent("beforetabchange", this, e, tab);
44360         if(e.cancel !== true && !tab.disabled){
44361             if(this.active){
44362                 this.active.hide();
44363             }
44364             this.active = this.items[id];
44365             this.active.show();
44366             this.fireEvent("tabchange", this, this.active);
44367         }
44368         return tab;
44369     },
44370
44371     /**
44372      * Gets the active {@link Roo.TabPanelItem}.
44373      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44374      */
44375     getActiveTab : function(){
44376         return this.active;
44377     },
44378
44379     /**
44380      * Updates the tab body element to fit the height of the container element
44381      * for overflow scrolling
44382      * @param {Number} targetHeight (optional) Override the starting height from the elements height
44383      */
44384     syncHeight : function(targetHeight){
44385         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44386         var bm = this.bodyEl.getMargins();
44387         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44388         this.bodyEl.setHeight(newHeight);
44389         return newHeight;
44390     },
44391
44392     onResize : function(){
44393         if(this.monitorResize){
44394             this.autoSizeTabs();
44395         }
44396     },
44397
44398     /**
44399      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44400      */
44401     beginUpdate : function(){
44402         this.updating = true;
44403     },
44404
44405     /**
44406      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44407      */
44408     endUpdate : function(){
44409         this.updating = false;
44410         this.autoSizeTabs();
44411     },
44412
44413     /**
44414      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44415      */
44416     autoSizeTabs : function()
44417     {
44418         var count = this.items.length;
44419         var vcount = count - this.hiddenCount;
44420         
44421         if (vcount < 2) {
44422             this.stripEl.hide();
44423         } else {
44424             this.stripEl.show();
44425         }
44426         
44427         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44428             return;
44429         }
44430         
44431         
44432         var w = Math.max(this.el.getWidth() - this.cpad, 10);
44433         var availWidth = Math.floor(w / vcount);
44434         var b = this.stripBody;
44435         if(b.getWidth() > w){
44436             var tabs = this.items;
44437             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44438             if(availWidth < this.minTabWidth){
44439                 /*if(!this.sleft){    // incomplete scrolling code
44440                     this.createScrollButtons();
44441                 }
44442                 this.showScroll();
44443                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44444             }
44445         }else{
44446             if(this.currentTabWidth < this.preferredTabWidth){
44447                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44448             }
44449         }
44450     },
44451
44452     /**
44453      * Returns the number of tabs in this TabPanel.
44454      * @return {Number}
44455      */
44456      getCount : function(){
44457          return this.items.length;
44458      },
44459
44460     /**
44461      * Resizes all the tabs to the passed width
44462      * @param {Number} The new width
44463      */
44464     setTabWidth : function(width){
44465         this.currentTabWidth = width;
44466         for(var i = 0, len = this.items.length; i < len; i++) {
44467                 if(!this.items[i].isHidden()) {
44468                 this.items[i].setWidth(width);
44469             }
44470         }
44471     },
44472
44473     /**
44474      * Destroys this TabPanel
44475      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44476      */
44477     destroy : function(removeEl){
44478         Roo.EventManager.removeResizeListener(this.onResize, this);
44479         for(var i = 0, len = this.items.length; i < len; i++){
44480             this.items[i].purgeListeners();
44481         }
44482         if(removeEl === true){
44483             this.el.update("");
44484             this.el.remove();
44485         }
44486     },
44487     
44488     createStrip : function(container)
44489     {
44490         var strip = document.createElement("nav");
44491         strip.className = Roo.bootstrap.version == 4 ?
44492             "navbar-light bg-light" : 
44493             "navbar navbar-default"; //"x-tabs-wrap";
44494         container.appendChild(strip);
44495         return strip;
44496     },
44497     
44498     createStripList : function(strip)
44499     {
44500         // div wrapper for retard IE
44501         // returns the "tr" element.
44502         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44503         //'<div class="x-tabs-strip-wrap">'+
44504           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44505           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44506         return strip.firstChild; //.firstChild.firstChild.firstChild;
44507     },
44508     createBody : function(container)
44509     {
44510         var body = document.createElement("div");
44511         Roo.id(body, "tab-body");
44512         //Roo.fly(body).addClass("x-tabs-body");
44513         Roo.fly(body).addClass("tab-content");
44514         container.appendChild(body);
44515         return body;
44516     },
44517     createItemBody :function(bodyEl, id){
44518         var body = Roo.getDom(id);
44519         if(!body){
44520             body = document.createElement("div");
44521             body.id = id;
44522         }
44523         //Roo.fly(body).addClass("x-tabs-item-body");
44524         Roo.fly(body).addClass("tab-pane");
44525          bodyEl.insertBefore(body, bodyEl.firstChild);
44526         return body;
44527     },
44528     /** @private */
44529     createStripElements :  function(stripEl, text, closable, tpl)
44530     {
44531         var td = document.createElement("li"); // was td..
44532         td.className = 'nav-item';
44533         
44534         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44535         
44536         
44537         stripEl.appendChild(td);
44538         /*if(closable){
44539             td.className = "x-tabs-closable";
44540             if(!this.closeTpl){
44541                 this.closeTpl = new Roo.Template(
44542                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44543                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44544                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
44545                 );
44546             }
44547             var el = this.closeTpl.overwrite(td, {"text": text});
44548             var close = el.getElementsByTagName("div")[0];
44549             var inner = el.getElementsByTagName("em")[0];
44550             return {"el": el, "close": close, "inner": inner};
44551         } else {
44552         */
44553         // not sure what this is..
44554 //            if(!this.tabTpl){
44555                 //this.tabTpl = new Roo.Template(
44556                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44557                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44558                 //);
44559 //                this.tabTpl = new Roo.Template(
44560 //                   '<a href="#">' +
44561 //                   '<span unselectable="on"' +
44562 //                            (this.disableTooltips ? '' : ' title="{text}"') +
44563 //                            ' >{text}</span></a>'
44564 //                );
44565 //                
44566 //            }
44567
44568
44569             var template = tpl || this.tabTpl || false;
44570             
44571             if(!template){
44572                 template =  new Roo.Template(
44573                         Roo.bootstrap.version == 4 ? 
44574                             (
44575                                 '<a class="nav-link" href="#" unselectable="on"' +
44576                                      (this.disableTooltips ? '' : ' title="{text}"') +
44577                                      ' >{text}</a>'
44578                             ) : (
44579                                 '<a class="nav-link" href="#">' +
44580                                 '<span unselectable="on"' +
44581                                          (this.disableTooltips ? '' : ' title="{text}"') +
44582                                     ' >{text}</span></a>'
44583                             )
44584                 );
44585             }
44586             
44587             switch (typeof(template)) {
44588                 case 'object' :
44589                     break;
44590                 case 'string' :
44591                     template = new Roo.Template(template);
44592                     break;
44593                 default :
44594                     break;
44595             }
44596             
44597             var el = template.overwrite(td, {"text": text});
44598             
44599             var inner = el.getElementsByTagName("span")[0];
44600             
44601             return {"el": el, "inner": inner};
44602             
44603     }
44604         
44605     
44606 });
44607
44608 /**
44609  * @class Roo.TabPanelItem
44610  * @extends Roo.util.Observable
44611  * Represents an individual item (tab plus body) in a TabPanel.
44612  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44613  * @param {String} id The id of this TabPanelItem
44614  * @param {String} text The text for the tab of this TabPanelItem
44615  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44616  */
44617 Roo.bootstrap.panel.TabItem = function(config){
44618     /**
44619      * The {@link Roo.TabPanel} this TabPanelItem belongs to
44620      * @type Roo.TabPanel
44621      */
44622     this.tabPanel = config.panel;
44623     /**
44624      * The id for this TabPanelItem
44625      * @type String
44626      */
44627     this.id = config.id;
44628     /** @private */
44629     this.disabled = false;
44630     /** @private */
44631     this.text = config.text;
44632     /** @private */
44633     this.loaded = false;
44634     this.closable = config.closable;
44635
44636     /**
44637      * The body element for this TabPanelItem.
44638      * @type Roo.Element
44639      */
44640     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44641     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44642     this.bodyEl.setStyle("display", "block");
44643     this.bodyEl.setStyle("zoom", "1");
44644     //this.hideAction();
44645
44646     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44647     /** @private */
44648     this.el = Roo.get(els.el);
44649     this.inner = Roo.get(els.inner, true);
44650      this.textEl = Roo.bootstrap.version == 4 ?
44651         this.el : Roo.get(this.el.dom.firstChild, true);
44652
44653     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44654     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44655
44656     
44657 //    this.el.on("mousedown", this.onTabMouseDown, this);
44658     this.el.on("click", this.onTabClick, this);
44659     /** @private */
44660     if(config.closable){
44661         var c = Roo.get(els.close, true);
44662         c.dom.title = this.closeText;
44663         c.addClassOnOver("close-over");
44664         c.on("click", this.closeClick, this);
44665      }
44666
44667     this.addEvents({
44668          /**
44669          * @event activate
44670          * Fires when this tab becomes the active tab.
44671          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44672          * @param {Roo.TabPanelItem} this
44673          */
44674         "activate": true,
44675         /**
44676          * @event beforeclose
44677          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44678          * @param {Roo.TabPanelItem} this
44679          * @param {Object} e Set cancel to true on this object to cancel the close.
44680          */
44681         "beforeclose": true,
44682         /**
44683          * @event close
44684          * Fires when this tab is closed.
44685          * @param {Roo.TabPanelItem} this
44686          */
44687          "close": true,
44688         /**
44689          * @event deactivate
44690          * Fires when this tab is no longer the active tab.
44691          * @param {Roo.TabPanel} tabPanel The parent TabPanel
44692          * @param {Roo.TabPanelItem} this
44693          */
44694          "deactivate" : true
44695     });
44696     this.hidden = false;
44697
44698     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44699 };
44700
44701 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44702            {
44703     purgeListeners : function(){
44704        Roo.util.Observable.prototype.purgeListeners.call(this);
44705        this.el.removeAllListeners();
44706     },
44707     /**
44708      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44709      */
44710     show : function(){
44711         this.status_node.addClass("active");
44712         this.showAction();
44713         if(Roo.isOpera){
44714             this.tabPanel.stripWrap.repaint();
44715         }
44716         this.fireEvent("activate", this.tabPanel, this);
44717     },
44718
44719     /**
44720      * Returns true if this tab is the active tab.
44721      * @return {Boolean}
44722      */
44723     isActive : function(){
44724         return this.tabPanel.getActiveTab() == this;
44725     },
44726
44727     /**
44728      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44729      */
44730     hide : function(){
44731         this.status_node.removeClass("active");
44732         this.hideAction();
44733         this.fireEvent("deactivate", this.tabPanel, this);
44734     },
44735
44736     hideAction : function(){
44737         this.bodyEl.hide();
44738         this.bodyEl.setStyle("position", "absolute");
44739         this.bodyEl.setLeft("-20000px");
44740         this.bodyEl.setTop("-20000px");
44741     },
44742
44743     showAction : function(){
44744         this.bodyEl.setStyle("position", "relative");
44745         this.bodyEl.setTop("");
44746         this.bodyEl.setLeft("");
44747         this.bodyEl.show();
44748     },
44749
44750     /**
44751      * Set the tooltip for the tab.
44752      * @param {String} tooltip The tab's tooltip
44753      */
44754     setTooltip : function(text){
44755         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44756             this.textEl.dom.qtip = text;
44757             this.textEl.dom.removeAttribute('title');
44758         }else{
44759             this.textEl.dom.title = text;
44760         }
44761     },
44762
44763     onTabClick : function(e){
44764         e.preventDefault();
44765         this.tabPanel.activate(this.id);
44766     },
44767
44768     onTabMouseDown : function(e){
44769         e.preventDefault();
44770         this.tabPanel.activate(this.id);
44771     },
44772 /*
44773     getWidth : function(){
44774         return this.inner.getWidth();
44775     },
44776
44777     setWidth : function(width){
44778         var iwidth = width - this.linode.getPadding("lr");
44779         this.inner.setWidth(iwidth);
44780         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44781         this.linode.setWidth(width);
44782     },
44783 */
44784     /**
44785      * Show or hide the tab
44786      * @param {Boolean} hidden True to hide or false to show.
44787      */
44788     setHidden : function(hidden){
44789         this.hidden = hidden;
44790         this.linode.setStyle("display", hidden ? "none" : "");
44791     },
44792
44793     /**
44794      * Returns true if this tab is "hidden"
44795      * @return {Boolean}
44796      */
44797     isHidden : function(){
44798         return this.hidden;
44799     },
44800
44801     /**
44802      * Returns the text for this tab
44803      * @return {String}
44804      */
44805     getText : function(){
44806         return this.text;
44807     },
44808     /*
44809     autoSize : function(){
44810         //this.el.beginMeasure();
44811         this.textEl.setWidth(1);
44812         /*
44813          *  #2804 [new] Tabs in Roojs
44814          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44815          */
44816         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44817         //this.el.endMeasure();
44818     //},
44819
44820     /**
44821      * Sets the text for the tab (Note: this also sets the tooltip text)
44822      * @param {String} text The tab's text and tooltip
44823      */
44824     setText : function(text){
44825         this.text = text;
44826         this.textEl.update(text);
44827         this.setTooltip(text);
44828         //if(!this.tabPanel.resizeTabs){
44829         //    this.autoSize();
44830         //}
44831     },
44832     /**
44833      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44834      */
44835     activate : function(){
44836         this.tabPanel.activate(this.id);
44837     },
44838
44839     /**
44840      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44841      */
44842     disable : function(){
44843         if(this.tabPanel.active != this){
44844             this.disabled = true;
44845             this.status_node.addClass("disabled");
44846         }
44847     },
44848
44849     /**
44850      * Enables this TabPanelItem if it was previously disabled.
44851      */
44852     enable : function(){
44853         this.disabled = false;
44854         this.status_node.removeClass("disabled");
44855     },
44856
44857     /**
44858      * Sets the content for this TabPanelItem.
44859      * @param {String} content The content
44860      * @param {Boolean} loadScripts true to look for and load scripts
44861      */
44862     setContent : function(content, loadScripts){
44863         this.bodyEl.update(content, loadScripts);
44864     },
44865
44866     /**
44867      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44868      * @return {Roo.UpdateManager} The UpdateManager
44869      */
44870     getUpdateManager : function(){
44871         return this.bodyEl.getUpdateManager();
44872     },
44873
44874     /**
44875      * Set a URL to be used to load the content for this TabPanelItem.
44876      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44877      * @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)
44878      * @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)
44879      * @return {Roo.UpdateManager} The UpdateManager
44880      */
44881     setUrl : function(url, params, loadOnce){
44882         if(this.refreshDelegate){
44883             this.un('activate', this.refreshDelegate);
44884         }
44885         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44886         this.on("activate", this.refreshDelegate);
44887         return this.bodyEl.getUpdateManager();
44888     },
44889
44890     /** @private */
44891     _handleRefresh : function(url, params, loadOnce){
44892         if(!loadOnce || !this.loaded){
44893             var updater = this.bodyEl.getUpdateManager();
44894             updater.update(url, params, this._setLoaded.createDelegate(this));
44895         }
44896     },
44897
44898     /**
44899      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
44900      *   Will fail silently if the setUrl method has not been called.
44901      *   This does not activate the panel, just updates its content.
44902      */
44903     refresh : function(){
44904         if(this.refreshDelegate){
44905            this.loaded = false;
44906            this.refreshDelegate();
44907         }
44908     },
44909
44910     /** @private */
44911     _setLoaded : function(){
44912         this.loaded = true;
44913     },
44914
44915     /** @private */
44916     closeClick : function(e){
44917         var o = {};
44918         e.stopEvent();
44919         this.fireEvent("beforeclose", this, o);
44920         if(o.cancel !== true){
44921             this.tabPanel.removeTab(this.id);
44922         }
44923     },
44924     /**
44925      * The text displayed in the tooltip for the close icon.
44926      * @type String
44927      */
44928     closeText : "Close this tab"
44929 });
44930 /**
44931 *    This script refer to:
44932 *    Title: International Telephone Input
44933 *    Author: Jack O'Connor
44934 *    Code version:  v12.1.12
44935 *    Availability: https://github.com/jackocnr/intl-tel-input.git
44936 **/
44937
44938 Roo.bootstrap.form.PhoneInputData = function() {
44939     var d = [
44940       [
44941         "Afghanistan (‫افغانستان‬‎)",
44942         "af",
44943         "93"
44944       ],
44945       [
44946         "Albania (Shqipëri)",
44947         "al",
44948         "355"
44949       ],
44950       [
44951         "Algeria (‫الجزائر‬‎)",
44952         "dz",
44953         "213"
44954       ],
44955       [
44956         "American Samoa",
44957         "as",
44958         "1684"
44959       ],
44960       [
44961         "Andorra",
44962         "ad",
44963         "376"
44964       ],
44965       [
44966         "Angola",
44967         "ao",
44968         "244"
44969       ],
44970       [
44971         "Anguilla",
44972         "ai",
44973         "1264"
44974       ],
44975       [
44976         "Antigua and Barbuda",
44977         "ag",
44978         "1268"
44979       ],
44980       [
44981         "Argentina",
44982         "ar",
44983         "54"
44984       ],
44985       [
44986         "Armenia (Հայաստան)",
44987         "am",
44988         "374"
44989       ],
44990       [
44991         "Aruba",
44992         "aw",
44993         "297"
44994       ],
44995       [
44996         "Australia",
44997         "au",
44998         "61",
44999         0
45000       ],
45001       [
45002         "Austria (Österreich)",
45003         "at",
45004         "43"
45005       ],
45006       [
45007         "Azerbaijan (Azərbaycan)",
45008         "az",
45009         "994"
45010       ],
45011       [
45012         "Bahamas",
45013         "bs",
45014         "1242"
45015       ],
45016       [
45017         "Bahrain (‫البحرين‬‎)",
45018         "bh",
45019         "973"
45020       ],
45021       [
45022         "Bangladesh (বাংলাদেশ)",
45023         "bd",
45024         "880"
45025       ],
45026       [
45027         "Barbados",
45028         "bb",
45029         "1246"
45030       ],
45031       [
45032         "Belarus (Беларусь)",
45033         "by",
45034         "375"
45035       ],
45036       [
45037         "Belgium (België)",
45038         "be",
45039         "32"
45040       ],
45041       [
45042         "Belize",
45043         "bz",
45044         "501"
45045       ],
45046       [
45047         "Benin (Bénin)",
45048         "bj",
45049         "229"
45050       ],
45051       [
45052         "Bermuda",
45053         "bm",
45054         "1441"
45055       ],
45056       [
45057         "Bhutan (འབྲུག)",
45058         "bt",
45059         "975"
45060       ],
45061       [
45062         "Bolivia",
45063         "bo",
45064         "591"
45065       ],
45066       [
45067         "Bosnia and Herzegovina (Босна и Херцеговина)",
45068         "ba",
45069         "387"
45070       ],
45071       [
45072         "Botswana",
45073         "bw",
45074         "267"
45075       ],
45076       [
45077         "Brazil (Brasil)",
45078         "br",
45079         "55"
45080       ],
45081       [
45082         "British Indian Ocean Territory",
45083         "io",
45084         "246"
45085       ],
45086       [
45087         "British Virgin Islands",
45088         "vg",
45089         "1284"
45090       ],
45091       [
45092         "Brunei",
45093         "bn",
45094         "673"
45095       ],
45096       [
45097         "Bulgaria (България)",
45098         "bg",
45099         "359"
45100       ],
45101       [
45102         "Burkina Faso",
45103         "bf",
45104         "226"
45105       ],
45106       [
45107         "Burundi (Uburundi)",
45108         "bi",
45109         "257"
45110       ],
45111       [
45112         "Cambodia (កម្ពុជា)",
45113         "kh",
45114         "855"
45115       ],
45116       [
45117         "Cameroon (Cameroun)",
45118         "cm",
45119         "237"
45120       ],
45121       [
45122         "Canada",
45123         "ca",
45124         "1",
45125         1,
45126         ["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"]
45127       ],
45128       [
45129         "Cape Verde (Kabu Verdi)",
45130         "cv",
45131         "238"
45132       ],
45133       [
45134         "Caribbean Netherlands",
45135         "bq",
45136         "599",
45137         1
45138       ],
45139       [
45140         "Cayman Islands",
45141         "ky",
45142         "1345"
45143       ],
45144       [
45145         "Central African Republic (République centrafricaine)",
45146         "cf",
45147         "236"
45148       ],
45149       [
45150         "Chad (Tchad)",
45151         "td",
45152         "235"
45153       ],
45154       [
45155         "Chile",
45156         "cl",
45157         "56"
45158       ],
45159       [
45160         "China (中国)",
45161         "cn",
45162         "86"
45163       ],
45164       [
45165         "Christmas Island",
45166         "cx",
45167         "61",
45168         2
45169       ],
45170       [
45171         "Cocos (Keeling) Islands",
45172         "cc",
45173         "61",
45174         1
45175       ],
45176       [
45177         "Colombia",
45178         "co",
45179         "57"
45180       ],
45181       [
45182         "Comoros (‫جزر القمر‬‎)",
45183         "km",
45184         "269"
45185       ],
45186       [
45187         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
45188         "cd",
45189         "243"
45190       ],
45191       [
45192         "Congo (Republic) (Congo-Brazzaville)",
45193         "cg",
45194         "242"
45195       ],
45196       [
45197         "Cook Islands",
45198         "ck",
45199         "682"
45200       ],
45201       [
45202         "Costa Rica",
45203         "cr",
45204         "506"
45205       ],
45206       [
45207         "Côte d’Ivoire",
45208         "ci",
45209         "225"
45210       ],
45211       [
45212         "Croatia (Hrvatska)",
45213         "hr",
45214         "385"
45215       ],
45216       [
45217         "Cuba",
45218         "cu",
45219         "53"
45220       ],
45221       [
45222         "Curaçao",
45223         "cw",
45224         "599",
45225         0
45226       ],
45227       [
45228         "Cyprus (Κύπρος)",
45229         "cy",
45230         "357"
45231       ],
45232       [
45233         "Czech Republic (Česká republika)",
45234         "cz",
45235         "420"
45236       ],
45237       [
45238         "Denmark (Danmark)",
45239         "dk",
45240         "45"
45241       ],
45242       [
45243         "Djibouti",
45244         "dj",
45245         "253"
45246       ],
45247       [
45248         "Dominica",
45249         "dm",
45250         "1767"
45251       ],
45252       [
45253         "Dominican Republic (República Dominicana)",
45254         "do",
45255         "1",
45256         2,
45257         ["809", "829", "849"]
45258       ],
45259       [
45260         "Ecuador",
45261         "ec",
45262         "593"
45263       ],
45264       [
45265         "Egypt (‫مصر‬‎)",
45266         "eg",
45267         "20"
45268       ],
45269       [
45270         "El Salvador",
45271         "sv",
45272         "503"
45273       ],
45274       [
45275         "Equatorial Guinea (Guinea Ecuatorial)",
45276         "gq",
45277         "240"
45278       ],
45279       [
45280         "Eritrea",
45281         "er",
45282         "291"
45283       ],
45284       [
45285         "Estonia (Eesti)",
45286         "ee",
45287         "372"
45288       ],
45289       [
45290         "Ethiopia",
45291         "et",
45292         "251"
45293       ],
45294       [
45295         "Falkland Islands (Islas Malvinas)",
45296         "fk",
45297         "500"
45298       ],
45299       [
45300         "Faroe Islands (Føroyar)",
45301         "fo",
45302         "298"
45303       ],
45304       [
45305         "Fiji",
45306         "fj",
45307         "679"
45308       ],
45309       [
45310         "Finland (Suomi)",
45311         "fi",
45312         "358",
45313         0
45314       ],
45315       [
45316         "France",
45317         "fr",
45318         "33"
45319       ],
45320       [
45321         "French Guiana (Guyane française)",
45322         "gf",
45323         "594"
45324       ],
45325       [
45326         "French Polynesia (Polynésie française)",
45327         "pf",
45328         "689"
45329       ],
45330       [
45331         "Gabon",
45332         "ga",
45333         "241"
45334       ],
45335       [
45336         "Gambia",
45337         "gm",
45338         "220"
45339       ],
45340       [
45341         "Georgia (საქართველო)",
45342         "ge",
45343         "995"
45344       ],
45345       [
45346         "Germany (Deutschland)",
45347         "de",
45348         "49"
45349       ],
45350       [
45351         "Ghana (Gaana)",
45352         "gh",
45353         "233"
45354       ],
45355       [
45356         "Gibraltar",
45357         "gi",
45358         "350"
45359       ],
45360       [
45361         "Greece (Ελλάδα)",
45362         "gr",
45363         "30"
45364       ],
45365       [
45366         "Greenland (Kalaallit Nunaat)",
45367         "gl",
45368         "299"
45369       ],
45370       [
45371         "Grenada",
45372         "gd",
45373         "1473"
45374       ],
45375       [
45376         "Guadeloupe",
45377         "gp",
45378         "590",
45379         0
45380       ],
45381       [
45382         "Guam",
45383         "gu",
45384         "1671"
45385       ],
45386       [
45387         "Guatemala",
45388         "gt",
45389         "502"
45390       ],
45391       [
45392         "Guernsey",
45393         "gg",
45394         "44",
45395         1
45396       ],
45397       [
45398         "Guinea (Guinée)",
45399         "gn",
45400         "224"
45401       ],
45402       [
45403         "Guinea-Bissau (Guiné Bissau)",
45404         "gw",
45405         "245"
45406       ],
45407       [
45408         "Guyana",
45409         "gy",
45410         "592"
45411       ],
45412       [
45413         "Haiti",
45414         "ht",
45415         "509"
45416       ],
45417       [
45418         "Honduras",
45419         "hn",
45420         "504"
45421       ],
45422       [
45423         "Hong Kong (香港)",
45424         "hk",
45425         "852"
45426       ],
45427       [
45428         "Hungary (Magyarország)",
45429         "hu",
45430         "36"
45431       ],
45432       [
45433         "Iceland (Ísland)",
45434         "is",
45435         "354"
45436       ],
45437       [
45438         "India (भारत)",
45439         "in",
45440         "91"
45441       ],
45442       [
45443         "Indonesia",
45444         "id",
45445         "62"
45446       ],
45447       [
45448         "Iran (‫ایران‬‎)",
45449         "ir",
45450         "98"
45451       ],
45452       [
45453         "Iraq (‫العراق‬‎)",
45454         "iq",
45455         "964"
45456       ],
45457       [
45458         "Ireland",
45459         "ie",
45460         "353"
45461       ],
45462       [
45463         "Isle of Man",
45464         "im",
45465         "44",
45466         2
45467       ],
45468       [
45469         "Israel (‫ישראל‬‎)",
45470         "il",
45471         "972"
45472       ],
45473       [
45474         "Italy (Italia)",
45475         "it",
45476         "39",
45477         0
45478       ],
45479       [
45480         "Jamaica",
45481         "jm",
45482         "1876"
45483       ],
45484       [
45485         "Japan (日本)",
45486         "jp",
45487         "81"
45488       ],
45489       [
45490         "Jersey",
45491         "je",
45492         "44",
45493         3
45494       ],
45495       [
45496         "Jordan (‫الأردن‬‎)",
45497         "jo",
45498         "962"
45499       ],
45500       [
45501         "Kazakhstan (Казахстан)",
45502         "kz",
45503         "7",
45504         1
45505       ],
45506       [
45507         "Kenya",
45508         "ke",
45509         "254"
45510       ],
45511       [
45512         "Kiribati",
45513         "ki",
45514         "686"
45515       ],
45516       [
45517         "Kosovo",
45518         "xk",
45519         "383"
45520       ],
45521       [
45522         "Kuwait (‫الكويت‬‎)",
45523         "kw",
45524         "965"
45525       ],
45526       [
45527         "Kyrgyzstan (Кыргызстан)",
45528         "kg",
45529         "996"
45530       ],
45531       [
45532         "Laos (ລາວ)",
45533         "la",
45534         "856"
45535       ],
45536       [
45537         "Latvia (Latvija)",
45538         "lv",
45539         "371"
45540       ],
45541       [
45542         "Lebanon (‫لبنان‬‎)",
45543         "lb",
45544         "961"
45545       ],
45546       [
45547         "Lesotho",
45548         "ls",
45549         "266"
45550       ],
45551       [
45552         "Liberia",
45553         "lr",
45554         "231"
45555       ],
45556       [
45557         "Libya (‫ليبيا‬‎)",
45558         "ly",
45559         "218"
45560       ],
45561       [
45562         "Liechtenstein",
45563         "li",
45564         "423"
45565       ],
45566       [
45567         "Lithuania (Lietuva)",
45568         "lt",
45569         "370"
45570       ],
45571       [
45572         "Luxembourg",
45573         "lu",
45574         "352"
45575       ],
45576       [
45577         "Macau (澳門)",
45578         "mo",
45579         "853"
45580       ],
45581       [
45582         "Macedonia (FYROM) (Македонија)",
45583         "mk",
45584         "389"
45585       ],
45586       [
45587         "Madagascar (Madagasikara)",
45588         "mg",
45589         "261"
45590       ],
45591       [
45592         "Malawi",
45593         "mw",
45594         "265"
45595       ],
45596       [
45597         "Malaysia",
45598         "my",
45599         "60"
45600       ],
45601       [
45602         "Maldives",
45603         "mv",
45604         "960"
45605       ],
45606       [
45607         "Mali",
45608         "ml",
45609         "223"
45610       ],
45611       [
45612         "Malta",
45613         "mt",
45614         "356"
45615       ],
45616       [
45617         "Marshall Islands",
45618         "mh",
45619         "692"
45620       ],
45621       [
45622         "Martinique",
45623         "mq",
45624         "596"
45625       ],
45626       [
45627         "Mauritania (‫موريتانيا‬‎)",
45628         "mr",
45629         "222"
45630       ],
45631       [
45632         "Mauritius (Moris)",
45633         "mu",
45634         "230"
45635       ],
45636       [
45637         "Mayotte",
45638         "yt",
45639         "262",
45640         1
45641       ],
45642       [
45643         "Mexico (México)",
45644         "mx",
45645         "52"
45646       ],
45647       [
45648         "Micronesia",
45649         "fm",
45650         "691"
45651       ],
45652       [
45653         "Moldova (Republica Moldova)",
45654         "md",
45655         "373"
45656       ],
45657       [
45658         "Monaco",
45659         "mc",
45660         "377"
45661       ],
45662       [
45663         "Mongolia (Монгол)",
45664         "mn",
45665         "976"
45666       ],
45667       [
45668         "Montenegro (Crna Gora)",
45669         "me",
45670         "382"
45671       ],
45672       [
45673         "Montserrat",
45674         "ms",
45675         "1664"
45676       ],
45677       [
45678         "Morocco (‫المغرب‬‎)",
45679         "ma",
45680         "212",
45681         0
45682       ],
45683       [
45684         "Mozambique (Moçambique)",
45685         "mz",
45686         "258"
45687       ],
45688       [
45689         "Myanmar (Burma) (မြန်မာ)",
45690         "mm",
45691         "95"
45692       ],
45693       [
45694         "Namibia (Namibië)",
45695         "na",
45696         "264"
45697       ],
45698       [
45699         "Nauru",
45700         "nr",
45701         "674"
45702       ],
45703       [
45704         "Nepal (नेपाल)",
45705         "np",
45706         "977"
45707       ],
45708       [
45709         "Netherlands (Nederland)",
45710         "nl",
45711         "31"
45712       ],
45713       [
45714         "New Caledonia (Nouvelle-Calédonie)",
45715         "nc",
45716         "687"
45717       ],
45718       [
45719         "New Zealand",
45720         "nz",
45721         "64"
45722       ],
45723       [
45724         "Nicaragua",
45725         "ni",
45726         "505"
45727       ],
45728       [
45729         "Niger (Nijar)",
45730         "ne",
45731         "227"
45732       ],
45733       [
45734         "Nigeria",
45735         "ng",
45736         "234"
45737       ],
45738       [
45739         "Niue",
45740         "nu",
45741         "683"
45742       ],
45743       [
45744         "Norfolk Island",
45745         "nf",
45746         "672"
45747       ],
45748       [
45749         "North Korea (조선 민주주의 인민 공화국)",
45750         "kp",
45751         "850"
45752       ],
45753       [
45754         "Northern Mariana Islands",
45755         "mp",
45756         "1670"
45757       ],
45758       [
45759         "Norway (Norge)",
45760         "no",
45761         "47",
45762         0
45763       ],
45764       [
45765         "Oman (‫عُمان‬‎)",
45766         "om",
45767         "968"
45768       ],
45769       [
45770         "Pakistan (‫پاکستان‬‎)",
45771         "pk",
45772         "92"
45773       ],
45774       [
45775         "Palau",
45776         "pw",
45777         "680"
45778       ],
45779       [
45780         "Palestine (‫فلسطين‬‎)",
45781         "ps",
45782         "970"
45783       ],
45784       [
45785         "Panama (Panamá)",
45786         "pa",
45787         "507"
45788       ],
45789       [
45790         "Papua New Guinea",
45791         "pg",
45792         "675"
45793       ],
45794       [
45795         "Paraguay",
45796         "py",
45797         "595"
45798       ],
45799       [
45800         "Peru (Perú)",
45801         "pe",
45802         "51"
45803       ],
45804       [
45805         "Philippines",
45806         "ph",
45807         "63"
45808       ],
45809       [
45810         "Poland (Polska)",
45811         "pl",
45812         "48"
45813       ],
45814       [
45815         "Portugal",
45816         "pt",
45817         "351"
45818       ],
45819       [
45820         "Puerto Rico",
45821         "pr",
45822         "1",
45823         3,
45824         ["787", "939"]
45825       ],
45826       [
45827         "Qatar (‫قطر‬‎)",
45828         "qa",
45829         "974"
45830       ],
45831       [
45832         "Réunion (La Réunion)",
45833         "re",
45834         "262",
45835         0
45836       ],
45837       [
45838         "Romania (România)",
45839         "ro",
45840         "40"
45841       ],
45842       [
45843         "Russia (Россия)",
45844         "ru",
45845         "7",
45846         0
45847       ],
45848       [
45849         "Rwanda",
45850         "rw",
45851         "250"
45852       ],
45853       [
45854         "Saint Barthélemy",
45855         "bl",
45856         "590",
45857         1
45858       ],
45859       [
45860         "Saint Helena",
45861         "sh",
45862         "290"
45863       ],
45864       [
45865         "Saint Kitts and Nevis",
45866         "kn",
45867         "1869"
45868       ],
45869       [
45870         "Saint Lucia",
45871         "lc",
45872         "1758"
45873       ],
45874       [
45875         "Saint Martin (Saint-Martin (partie française))",
45876         "mf",
45877         "590",
45878         2
45879       ],
45880       [
45881         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45882         "pm",
45883         "508"
45884       ],
45885       [
45886         "Saint Vincent and the Grenadines",
45887         "vc",
45888         "1784"
45889       ],
45890       [
45891         "Samoa",
45892         "ws",
45893         "685"
45894       ],
45895       [
45896         "San Marino",
45897         "sm",
45898         "378"
45899       ],
45900       [
45901         "São Tomé and Príncipe (São Tomé e Príncipe)",
45902         "st",
45903         "239"
45904       ],
45905       [
45906         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
45907         "sa",
45908         "966"
45909       ],
45910       [
45911         "Senegal (Sénégal)",
45912         "sn",
45913         "221"
45914       ],
45915       [
45916         "Serbia (Србија)",
45917         "rs",
45918         "381"
45919       ],
45920       [
45921         "Seychelles",
45922         "sc",
45923         "248"
45924       ],
45925       [
45926         "Sierra Leone",
45927         "sl",
45928         "232"
45929       ],
45930       [
45931         "Singapore",
45932         "sg",
45933         "65"
45934       ],
45935       [
45936         "Sint Maarten",
45937         "sx",
45938         "1721"
45939       ],
45940       [
45941         "Slovakia (Slovensko)",
45942         "sk",
45943         "421"
45944       ],
45945       [
45946         "Slovenia (Slovenija)",
45947         "si",
45948         "386"
45949       ],
45950       [
45951         "Solomon Islands",
45952         "sb",
45953         "677"
45954       ],
45955       [
45956         "Somalia (Soomaaliya)",
45957         "so",
45958         "252"
45959       ],
45960       [
45961         "South Africa",
45962         "za",
45963         "27"
45964       ],
45965       [
45966         "South Korea (대한민국)",
45967         "kr",
45968         "82"
45969       ],
45970       [
45971         "South Sudan (‫جنوب السودان‬‎)",
45972         "ss",
45973         "211"
45974       ],
45975       [
45976         "Spain (España)",
45977         "es",
45978         "34"
45979       ],
45980       [
45981         "Sri Lanka (ශ්‍රී ලංකාව)",
45982         "lk",
45983         "94"
45984       ],
45985       [
45986         "Sudan (‫السودان‬‎)",
45987         "sd",
45988         "249"
45989       ],
45990       [
45991         "Suriname",
45992         "sr",
45993         "597"
45994       ],
45995       [
45996         "Svalbard and Jan Mayen",
45997         "sj",
45998         "47",
45999         1
46000       ],
46001       [
46002         "Swaziland",
46003         "sz",
46004         "268"
46005       ],
46006       [
46007         "Sweden (Sverige)",
46008         "se",
46009         "46"
46010       ],
46011       [
46012         "Switzerland (Schweiz)",
46013         "ch",
46014         "41"
46015       ],
46016       [
46017         "Syria (‫سوريا‬‎)",
46018         "sy",
46019         "963"
46020       ],
46021       [
46022         "Taiwan (台灣)",
46023         "tw",
46024         "886"
46025       ],
46026       [
46027         "Tajikistan",
46028         "tj",
46029         "992"
46030       ],
46031       [
46032         "Tanzania",
46033         "tz",
46034         "255"
46035       ],
46036       [
46037         "Thailand (ไทย)",
46038         "th",
46039         "66"
46040       ],
46041       [
46042         "Timor-Leste",
46043         "tl",
46044         "670"
46045       ],
46046       [
46047         "Togo",
46048         "tg",
46049         "228"
46050       ],
46051       [
46052         "Tokelau",
46053         "tk",
46054         "690"
46055       ],
46056       [
46057         "Tonga",
46058         "to",
46059         "676"
46060       ],
46061       [
46062         "Trinidad and Tobago",
46063         "tt",
46064         "1868"
46065       ],
46066       [
46067         "Tunisia (‫تونس‬‎)",
46068         "tn",
46069         "216"
46070       ],
46071       [
46072         "Turkey (Türkiye)",
46073         "tr",
46074         "90"
46075       ],
46076       [
46077         "Turkmenistan",
46078         "tm",
46079         "993"
46080       ],
46081       [
46082         "Turks and Caicos Islands",
46083         "tc",
46084         "1649"
46085       ],
46086       [
46087         "Tuvalu",
46088         "tv",
46089         "688"
46090       ],
46091       [
46092         "U.S. Virgin Islands",
46093         "vi",
46094         "1340"
46095       ],
46096       [
46097         "Uganda",
46098         "ug",
46099         "256"
46100       ],
46101       [
46102         "Ukraine (Україна)",
46103         "ua",
46104         "380"
46105       ],
46106       [
46107         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
46108         "ae",
46109         "971"
46110       ],
46111       [
46112         "United Kingdom",
46113         "gb",
46114         "44",
46115         0
46116       ],
46117       [
46118         "United States",
46119         "us",
46120         "1",
46121         0
46122       ],
46123       [
46124         "Uruguay",
46125         "uy",
46126         "598"
46127       ],
46128       [
46129         "Uzbekistan (Oʻzbekiston)",
46130         "uz",
46131         "998"
46132       ],
46133       [
46134         "Vanuatu",
46135         "vu",
46136         "678"
46137       ],
46138       [
46139         "Vatican City (Città del Vaticano)",
46140         "va",
46141         "39",
46142         1
46143       ],
46144       [
46145         "Venezuela",
46146         "ve",
46147         "58"
46148       ],
46149       [
46150         "Vietnam (Việt Nam)",
46151         "vn",
46152         "84"
46153       ],
46154       [
46155         "Wallis and Futuna (Wallis-et-Futuna)",
46156         "wf",
46157         "681"
46158       ],
46159       [
46160         "Western Sahara (‫الصحراء الغربية‬‎)",
46161         "eh",
46162         "212",
46163         1
46164       ],
46165       [
46166         "Yemen (‫اليمن‬‎)",
46167         "ye",
46168         "967"
46169       ],
46170       [
46171         "Zambia",
46172         "zm",
46173         "260"
46174       ],
46175       [
46176         "Zimbabwe",
46177         "zw",
46178         "263"
46179       ],
46180       [
46181         "Åland Islands",
46182         "ax",
46183         "358",
46184         1
46185       ]
46186   ];
46187   
46188   return d;
46189 }/**
46190 *    This script refer to:
46191 *    Title: International Telephone Input
46192 *    Author: Jack O'Connor
46193 *    Code version:  v12.1.12
46194 *    Availability: https://github.com/jackocnr/intl-tel-input.git
46195 **/
46196
46197 /**
46198  * @class Roo.bootstrap.form.PhoneInput
46199  * @extends Roo.bootstrap.form.TriggerField
46200  * An input with International dial-code selection
46201  
46202  * @cfg {String} defaultDialCode default '+852'
46203  * @cfg {Array} preferedCountries default []
46204   
46205  * @constructor
46206  * Create a new PhoneInput.
46207  * @param {Object} config Configuration options
46208  */
46209
46210 Roo.bootstrap.form.PhoneInput = function(config) {
46211     Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46212 };
46213
46214 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46215         /**
46216         * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46217         */
46218         listWidth: undefined,
46219         
46220         selectedClass: 'active',
46221         
46222         invalidClass : "has-warning",
46223         
46224         validClass: 'has-success',
46225         
46226         allowed: '0123456789',
46227         
46228         max_length: 15,
46229         
46230         /**
46231          * @cfg {String} defaultDialCode The default dial code when initializing the input
46232          */
46233         defaultDialCode: '+852',
46234         
46235         /**
46236          * @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
46237          */
46238         preferedCountries: false,
46239         
46240         getAutoCreate : function()
46241         {
46242             var data = Roo.bootstrap.form.PhoneInputData();
46243             var align = this.labelAlign || this.parentLabelAlign();
46244             var id = Roo.id();
46245             
46246             this.allCountries = [];
46247             this.dialCodeMapping = [];
46248             
46249             for (var i = 0; i < data.length; i++) {
46250               var c = data[i];
46251               this.allCountries[i] = {
46252                 name: c[0],
46253                 iso2: c[1],
46254                 dialCode: c[2],
46255                 priority: c[3] || 0,
46256                 areaCodes: c[4] || null
46257               };
46258               this.dialCodeMapping[c[2]] = {
46259                   name: c[0],
46260                   iso2: c[1],
46261                   priority: c[3] || 0,
46262                   areaCodes: c[4] || null
46263               };
46264             }
46265             
46266             var cfg = {
46267                 cls: 'form-group',
46268                 cn: []
46269             };
46270             
46271             var input =  {
46272                 tag: 'input',
46273                 id : id,
46274                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46275                 maxlength: this.max_length,
46276                 cls : 'form-control tel-input',
46277                 autocomplete: 'new-password'
46278             };
46279             
46280             var hiddenInput = {
46281                 tag: 'input',
46282                 type: 'hidden',
46283                 cls: 'hidden-tel-input'
46284             };
46285             
46286             if (this.name) {
46287                 hiddenInput.name = this.name;
46288             }
46289             
46290             if (this.disabled) {
46291                 input.disabled = true;
46292             }
46293             
46294             var flag_container = {
46295                 tag: 'div',
46296                 cls: 'flag-box',
46297                 cn: [
46298                     {
46299                         tag: 'div',
46300                         cls: 'flag'
46301                     },
46302                     {
46303                         tag: 'div',
46304                         cls: 'caret'
46305                     }
46306                 ]
46307             };
46308             
46309             var box = {
46310                 tag: 'div',
46311                 cls: this.hasFeedback ? 'has-feedback' : '',
46312                 cn: [
46313                     hiddenInput,
46314                     input,
46315                     {
46316                         tag: 'input',
46317                         cls: 'dial-code-holder',
46318                         disabled: true
46319                     }
46320                 ]
46321             };
46322             
46323             var container = {
46324                 cls: 'roo-select2-container input-group',
46325                 cn: [
46326                     flag_container,
46327                     box
46328                 ]
46329             };
46330             
46331             if (this.fieldLabel.length) {
46332                 var indicator = {
46333                     tag: 'i',
46334                     tooltip: 'This field is required'
46335                 };
46336                 
46337                 var label = {
46338                     tag: 'label',
46339                     'for':  id,
46340                     cls: 'control-label',
46341                     cn: []
46342                 };
46343                 
46344                 var label_text = {
46345                     tag: 'span',
46346                     html: this.fieldLabel
46347                 };
46348                 
46349                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46350                 label.cn = [
46351                     indicator,
46352                     label_text
46353                 ];
46354                 
46355                 if(this.indicatorpos == 'right') {
46356                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46357                     label.cn = [
46358                         label_text,
46359                         indicator
46360                     ];
46361                 }
46362                 
46363                 if(align == 'left') {
46364                     container = {
46365                         tag: 'div',
46366                         cn: [
46367                             container
46368                         ]
46369                     };
46370                     
46371                     if(this.labelWidth > 12){
46372                         label.style = "width: " + this.labelWidth + 'px';
46373                     }
46374                     if(this.labelWidth < 13 && this.labelmd == 0){
46375                         this.labelmd = this.labelWidth;
46376                     }
46377                     if(this.labellg > 0){
46378                         label.cls += ' col-lg-' + this.labellg;
46379                         input.cls += ' col-lg-' + (12 - this.labellg);
46380                     }
46381                     if(this.labelmd > 0){
46382                         label.cls += ' col-md-' + this.labelmd;
46383                         container.cls += ' col-md-' + (12 - this.labelmd);
46384                     }
46385                     if(this.labelsm > 0){
46386                         label.cls += ' col-sm-' + this.labelsm;
46387                         container.cls += ' col-sm-' + (12 - this.labelsm);
46388                     }
46389                     if(this.labelxs > 0){
46390                         label.cls += ' col-xs-' + this.labelxs;
46391                         container.cls += ' col-xs-' + (12 - this.labelxs);
46392                     }
46393                 }
46394             }
46395             
46396             cfg.cn = [
46397                 label,
46398                 container
46399             ];
46400             
46401             var settings = this;
46402             
46403             ['xs','sm','md','lg'].map(function(size){
46404                 if (settings[size]) {
46405                     cfg.cls += ' col-' + size + '-' + settings[size];
46406                 }
46407             });
46408             
46409             this.store = new Roo.data.Store({
46410                 proxy : new Roo.data.MemoryProxy({}),
46411                 reader : new Roo.data.JsonReader({
46412                     fields : [
46413                         {
46414                             'name' : 'name',
46415                             'type' : 'string'
46416                         },
46417                         {
46418                             'name' : 'iso2',
46419                             'type' : 'string'
46420                         },
46421                         {
46422                             'name' : 'dialCode',
46423                             'type' : 'string'
46424                         },
46425                         {
46426                             'name' : 'priority',
46427                             'type' : 'string'
46428                         },
46429                         {
46430                             'name' : 'areaCodes',
46431                             'type' : 'string'
46432                         }
46433                     ]
46434                 })
46435             });
46436             
46437             if(!this.preferedCountries) {
46438                 this.preferedCountries = [
46439                     'hk',
46440                     'gb',
46441                     'us'
46442                 ];
46443             }
46444             
46445             var p = this.preferedCountries.reverse();
46446             
46447             if(p) {
46448                 for (var i = 0; i < p.length; i++) {
46449                     for (var j = 0; j < this.allCountries.length; j++) {
46450                         if(this.allCountries[j].iso2 == p[i]) {
46451                             var t = this.allCountries[j];
46452                             this.allCountries.splice(j,1);
46453                             this.allCountries.unshift(t);
46454                         }
46455                     } 
46456                 }
46457             }
46458             
46459             this.store.proxy.data = {
46460                 success: true,
46461                 data: this.allCountries
46462             };
46463             
46464             return cfg;
46465         },
46466         
46467         initEvents : function()
46468         {
46469             this.createList();
46470             Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46471             
46472             this.indicator = this.indicatorEl();
46473             this.flag = this.flagEl();
46474             this.dialCodeHolder = this.dialCodeHolderEl();
46475             
46476             this.trigger = this.el.select('div.flag-box',true).first();
46477             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46478             
46479             var _this = this;
46480             
46481             (function(){
46482                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46483                 _this.list.setWidth(lw);
46484             }).defer(100);
46485             
46486             this.list.on('mouseover', this.onViewOver, this);
46487             this.list.on('mousemove', this.onViewMove, this);
46488             this.inputEl().on("keyup", this.onKeyUp, this);
46489             this.inputEl().on("keypress", this.onKeyPress, this);
46490             
46491             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46492
46493             this.view = new Roo.View(this.list, this.tpl, {
46494                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46495             });
46496             
46497             this.view.on('click', this.onViewClick, this);
46498             this.setValue(this.defaultDialCode);
46499         },
46500         
46501         onTriggerClick : function(e)
46502         {
46503             Roo.log('trigger click');
46504             if(this.disabled){
46505                 return;
46506             }
46507             
46508             if(this.isExpanded()){
46509                 this.collapse();
46510                 this.hasFocus = false;
46511             }else {
46512                 this.store.load({});
46513                 this.hasFocus = true;
46514                 this.expand();
46515             }
46516         },
46517         
46518         isExpanded : function()
46519         {
46520             return this.list.isVisible();
46521         },
46522         
46523         collapse : function()
46524         {
46525             if(!this.isExpanded()){
46526                 return;
46527             }
46528             this.list.hide();
46529             Roo.get(document).un('mousedown', this.collapseIf, this);
46530             Roo.get(document).un('mousewheel', this.collapseIf, this);
46531             this.fireEvent('collapse', this);
46532             this.validate();
46533         },
46534         
46535         expand : function()
46536         {
46537             Roo.log('expand');
46538
46539             if(this.isExpanded() || !this.hasFocus){
46540                 return;
46541             }
46542             
46543             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46544             this.list.setWidth(lw);
46545             
46546             this.list.show();
46547             this.restrictHeight();
46548             
46549             Roo.get(document).on('mousedown', this.collapseIf, this);
46550             Roo.get(document).on('mousewheel', this.collapseIf, this);
46551             
46552             this.fireEvent('expand', this);
46553         },
46554         
46555         restrictHeight : function()
46556         {
46557             this.list.alignTo(this.inputEl(), this.listAlign);
46558             this.list.alignTo(this.inputEl(), this.listAlign);
46559         },
46560         
46561         onViewOver : function(e, t)
46562         {
46563             if(this.inKeyMode){
46564                 return;
46565             }
46566             var item = this.view.findItemFromChild(t);
46567             
46568             if(item){
46569                 var index = this.view.indexOf(item);
46570                 this.select(index, false);
46571             }
46572         },
46573
46574         // private
46575         onViewClick : function(view, doFocus, el, e)
46576         {
46577             var index = this.view.getSelectedIndexes()[0];
46578             
46579             var r = this.store.getAt(index);
46580             
46581             if(r){
46582                 this.onSelect(r, index);
46583             }
46584             if(doFocus !== false && !this.blockFocus){
46585                 this.inputEl().focus();
46586             }
46587         },
46588         
46589         onViewMove : function(e, t)
46590         {
46591             this.inKeyMode = false;
46592         },
46593         
46594         select : function(index, scrollIntoView)
46595         {
46596             this.selectedIndex = index;
46597             this.view.select(index);
46598             if(scrollIntoView !== false){
46599                 var el = this.view.getNode(index);
46600                 if(el){
46601                     this.list.scrollChildIntoView(el, false);
46602                 }
46603             }
46604         },
46605         
46606         createList : function()
46607         {
46608             this.list = Roo.get(document.body).createChild({
46609                 tag: 'ul',
46610                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46611                 style: 'display:none'
46612             });
46613             
46614             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46615         },
46616         
46617         collapseIf : function(e)
46618         {
46619             var in_combo  = e.within(this.el);
46620             var in_list =  e.within(this.list);
46621             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46622             
46623             if (in_combo || in_list || is_list) {
46624                 return;
46625             }
46626             this.collapse();
46627         },
46628         
46629         onSelect : function(record, index)
46630         {
46631             if(this.fireEvent('beforeselect', this, record, index) !== false){
46632                 
46633                 this.setFlagClass(record.data.iso2);
46634                 this.setDialCode(record.data.dialCode);
46635                 this.hasFocus = false;
46636                 this.collapse();
46637                 this.fireEvent('select', this, record, index);
46638             }
46639         },
46640         
46641         flagEl : function()
46642         {
46643             var flag = this.el.select('div.flag',true).first();
46644             if(!flag){
46645                 return false;
46646             }
46647             return flag;
46648         },
46649         
46650         dialCodeHolderEl : function()
46651         {
46652             var d = this.el.select('input.dial-code-holder',true).first();
46653             if(!d){
46654                 return false;
46655             }
46656             return d;
46657         },
46658         
46659         setDialCode : function(v)
46660         {
46661             this.dialCodeHolder.dom.value = '+'+v;
46662         },
46663         
46664         setFlagClass : function(n)
46665         {
46666             this.flag.dom.className = 'flag '+n;
46667         },
46668         
46669         getValue : function()
46670         {
46671             var v = this.inputEl().getValue();
46672             if(this.dialCodeHolder) {
46673                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46674             }
46675             return v;
46676         },
46677         
46678         setValue : function(v)
46679         {
46680             var d = this.getDialCode(v);
46681             
46682             //invalid dial code
46683             if(v.length == 0 || !d || d.length == 0) {
46684                 if(this.rendered){
46685                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46686                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46687                 }
46688                 return;
46689             }
46690             
46691             //valid dial code
46692             this.setFlagClass(this.dialCodeMapping[d].iso2);
46693             this.setDialCode(d);
46694             this.inputEl().dom.value = v.replace('+'+d,'');
46695             this.hiddenEl().dom.value = this.getValue();
46696             
46697             this.validate();
46698         },
46699         
46700         getDialCode : function(v)
46701         {
46702             v = v ||  '';
46703             
46704             if (v.length == 0) {
46705                 return this.dialCodeHolder.dom.value;
46706             }
46707             
46708             var dialCode = "";
46709             if (v.charAt(0) != "+") {
46710                 return false;
46711             }
46712             var numericChars = "";
46713             for (var i = 1; i < v.length; i++) {
46714               var c = v.charAt(i);
46715               if (!isNaN(c)) {
46716                 numericChars += c;
46717                 if (this.dialCodeMapping[numericChars]) {
46718                   dialCode = v.substr(1, i);
46719                 }
46720                 if (numericChars.length == 4) {
46721                   break;
46722                 }
46723               }
46724             }
46725             return dialCode;
46726         },
46727         
46728         reset : function()
46729         {
46730             this.setValue(this.defaultDialCode);
46731             this.validate();
46732         },
46733         
46734         hiddenEl : function()
46735         {
46736             return this.el.select('input.hidden-tel-input',true).first();
46737         },
46738         
46739         // after setting val
46740         onKeyUp : function(e){
46741             this.setValue(this.getValue());
46742         },
46743         
46744         onKeyPress : function(e){
46745             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46746                 e.stopEvent();
46747             }
46748         }
46749         
46750 });
46751 /**
46752  * @class Roo.bootstrap.form.MoneyField
46753  * @extends Roo.bootstrap.form.ComboBox
46754  * Bootstrap MoneyField class
46755  * 
46756  * @constructor
46757  * Create a new MoneyField.
46758  * @param {Object} config Configuration options
46759  */
46760
46761 Roo.bootstrap.form.MoneyField = function(config) {
46762     
46763     Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46764     
46765 };
46766
46767 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46768     
46769     /**
46770      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46771      */
46772     allowDecimals : true,
46773     /**
46774      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46775      */
46776     decimalSeparator : ".",
46777     /**
46778      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46779      */
46780     decimalPrecision : 0,
46781     /**
46782      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46783      */
46784     allowNegative : true,
46785     /**
46786      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46787      */
46788     allowZero: true,
46789     /**
46790      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46791      */
46792     minValue : Number.NEGATIVE_INFINITY,
46793     /**
46794      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46795      */
46796     maxValue : Number.MAX_VALUE,
46797     /**
46798      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46799      */
46800     minText : "The minimum value for this field is {0}",
46801     /**
46802      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46803      */
46804     maxText : "The maximum value for this field is {0}",
46805     /**
46806      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
46807      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46808      */
46809     nanText : "{0} is not a valid number",
46810     /**
46811      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46812      */
46813     castInt : true,
46814     /**
46815      * @cfg {String} defaults currency of the MoneyField
46816      * value should be in lkey
46817      */
46818     defaultCurrency : false,
46819     /**
46820      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46821      */
46822     thousandsDelimiter : false,
46823     /**
46824      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46825      */
46826     max_length: false,
46827     
46828     inputlg : 9,
46829     inputmd : 9,
46830     inputsm : 9,
46831     inputxs : 6,
46832      /**
46833      * @cfg {Roo.data.Store} store  Store to lookup currency??
46834      */
46835     store : false,
46836     
46837     getAutoCreate : function()
46838     {
46839         var align = this.labelAlign || this.parentLabelAlign();
46840         
46841         var id = Roo.id();
46842
46843         var cfg = {
46844             cls: 'form-group',
46845             cn: []
46846         };
46847
46848         var input =  {
46849             tag: 'input',
46850             id : id,
46851             cls : 'form-control roo-money-amount-input',
46852             autocomplete: 'new-password'
46853         };
46854         
46855         var hiddenInput = {
46856             tag: 'input',
46857             type: 'hidden',
46858             id: Roo.id(),
46859             cls: 'hidden-number-input'
46860         };
46861         
46862         if(this.max_length) {
46863             input.maxlength = this.max_length; 
46864         }
46865         
46866         if (this.name) {
46867             hiddenInput.name = this.name;
46868         }
46869
46870         if (this.disabled) {
46871             input.disabled = true;
46872         }
46873
46874         var clg = 12 - this.inputlg;
46875         var cmd = 12 - this.inputmd;
46876         var csm = 12 - this.inputsm;
46877         var cxs = 12 - this.inputxs;
46878         
46879         var container = {
46880             tag : 'div',
46881             cls : 'row roo-money-field',
46882             cn : [
46883                 {
46884                     tag : 'div',
46885                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46886                     cn : [
46887                         {
46888                             tag : 'div',
46889                             cls: 'roo-select2-container input-group',
46890                             cn: [
46891                                 {
46892                                     tag : 'input',
46893                                     cls : 'form-control roo-money-currency-input',
46894                                     autocomplete: 'new-password',
46895                                     readOnly : 1,
46896                                     name : this.currencyName
46897                                 },
46898                                 {
46899                                     tag :'span',
46900                                     cls : 'input-group-addon',
46901                                     cn : [
46902                                         {
46903                                             tag: 'span',
46904                                             cls: 'caret'
46905                                         }
46906                                     ]
46907                                 }
46908                             ]
46909                         }
46910                     ]
46911                 },
46912                 {
46913                     tag : 'div',
46914                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46915                     cn : [
46916                         {
46917                             tag: 'div',
46918                             cls: this.hasFeedback ? 'has-feedback' : '',
46919                             cn: [
46920                                 input
46921                             ]
46922                         }
46923                     ]
46924                 }
46925             ]
46926             
46927         };
46928         
46929         if (this.fieldLabel.length) {
46930             var indicator = {
46931                 tag: 'i',
46932                 tooltip: 'This field is required'
46933             };
46934
46935             var label = {
46936                 tag: 'label',
46937                 'for':  id,
46938                 cls: 'control-label',
46939                 cn: []
46940             };
46941
46942             var label_text = {
46943                 tag: 'span',
46944                 html: this.fieldLabel
46945             };
46946
46947             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46948             label.cn = [
46949                 indicator,
46950                 label_text
46951             ];
46952
46953             if(this.indicatorpos == 'right') {
46954                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46955                 label.cn = [
46956                     label_text,
46957                     indicator
46958                 ];
46959             }
46960
46961             if(align == 'left') {
46962                 container = {
46963                     tag: 'div',
46964                     cn: [
46965                         container
46966                     ]
46967                 };
46968
46969                 if(this.labelWidth > 12){
46970                     label.style = "width: " + this.labelWidth + 'px';
46971                 }
46972                 if(this.labelWidth < 13 && this.labelmd == 0){
46973                     this.labelmd = this.labelWidth;
46974                 }
46975                 if(this.labellg > 0){
46976                     label.cls += ' col-lg-' + this.labellg;
46977                     input.cls += ' col-lg-' + (12 - this.labellg);
46978                 }
46979                 if(this.labelmd > 0){
46980                     label.cls += ' col-md-' + this.labelmd;
46981                     container.cls += ' col-md-' + (12 - this.labelmd);
46982                 }
46983                 if(this.labelsm > 0){
46984                     label.cls += ' col-sm-' + this.labelsm;
46985                     container.cls += ' col-sm-' + (12 - this.labelsm);
46986                 }
46987                 if(this.labelxs > 0){
46988                     label.cls += ' col-xs-' + this.labelxs;
46989                     container.cls += ' col-xs-' + (12 - this.labelxs);
46990                 }
46991             }
46992         }
46993
46994         cfg.cn = [
46995             label,
46996             container,
46997             hiddenInput
46998         ];
46999         
47000         var settings = this;
47001
47002         ['xs','sm','md','lg'].map(function(size){
47003             if (settings[size]) {
47004                 cfg.cls += ' col-' + size + '-' + settings[size];
47005             }
47006         });
47007         
47008         return cfg;
47009     },
47010     
47011     initEvents : function()
47012     {
47013         this.indicator = this.indicatorEl();
47014         
47015         this.initCurrencyEvent();
47016         
47017         this.initNumberEvent();
47018     },
47019     
47020     initCurrencyEvent : function()
47021     {
47022         if (!this.store) {
47023             throw "can not find store for combo";
47024         }
47025         
47026         this.store = Roo.factory(this.store, Roo.data);
47027         this.store.parent = this;
47028         
47029         this.createList();
47030         
47031         this.triggerEl = this.el.select('.input-group-addon', true).first();
47032         
47033         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
47034         
47035         var _this = this;
47036         
47037         (function(){
47038             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47039             _this.list.setWidth(lw);
47040         }).defer(100);
47041         
47042         this.list.on('mouseover', this.onViewOver, this);
47043         this.list.on('mousemove', this.onViewMove, this);
47044         this.list.on('scroll', this.onViewScroll, this);
47045         
47046         if(!this.tpl){
47047             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
47048         }
47049         
47050         this.view = new Roo.View(this.list, this.tpl, {
47051             singleSelect:true, store: this.store, selectedClass: this.selectedClass
47052         });
47053         
47054         this.view.on('click', this.onViewClick, this);
47055         
47056         this.store.on('beforeload', this.onBeforeLoad, this);
47057         this.store.on('load', this.onLoad, this);
47058         this.store.on('loadexception', this.onLoadException, this);
47059         
47060         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
47061             "up" : function(e){
47062                 this.inKeyMode = true;
47063                 this.selectPrev();
47064             },
47065
47066             "down" : function(e){
47067                 if(!this.isExpanded()){
47068                     this.onTriggerClick();
47069                 }else{
47070                     this.inKeyMode = true;
47071                     this.selectNext();
47072                 }
47073             },
47074
47075             "enter" : function(e){
47076                 this.collapse();
47077                 
47078                 if(this.fireEvent("specialkey", this, e)){
47079                     this.onViewClick(false);
47080                 }
47081                 
47082                 return true;
47083             },
47084
47085             "esc" : function(e){
47086                 this.collapse();
47087             },
47088
47089             "tab" : function(e){
47090                 this.collapse();
47091                 
47092                 if(this.fireEvent("specialkey", this, e)){
47093                     this.onViewClick(false);
47094                 }
47095                 
47096                 return true;
47097             },
47098
47099             scope : this,
47100
47101             doRelay : function(foo, bar, hname){
47102                 if(hname == 'down' || this.scope.isExpanded()){
47103                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47104                 }
47105                 return true;
47106             },
47107
47108             forceKeyDown: true
47109         });
47110         
47111         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
47112         
47113     },
47114     
47115     initNumberEvent : function(e)
47116     {
47117         this.inputEl().on("keydown" , this.fireKey,  this);
47118         this.inputEl().on("focus", this.onFocus,  this);
47119         this.inputEl().on("blur", this.onBlur,  this);
47120         
47121         this.inputEl().relayEvent('keyup', this);
47122         
47123         if(this.indicator){
47124             this.indicator.addClass('invisible');
47125         }
47126  
47127         this.originalValue = this.getValue();
47128         
47129         if(this.validationEvent == 'keyup'){
47130             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
47131             this.inputEl().on('keyup', this.filterValidation, this);
47132         }
47133         else if(this.validationEvent !== false){
47134             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
47135         }
47136         
47137         if(this.selectOnFocus){
47138             this.on("focus", this.preFocus, this);
47139             
47140         }
47141         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
47142             this.inputEl().on("keypress", this.filterKeys, this);
47143         } else {
47144             this.inputEl().relayEvent('keypress', this);
47145         }
47146         
47147         var allowed = "0123456789";
47148         
47149         if(this.allowDecimals){
47150             allowed += this.decimalSeparator;
47151         }
47152         
47153         if(this.allowNegative){
47154             allowed += "-";
47155         }
47156         
47157         if(this.thousandsDelimiter) {
47158             allowed += ",";
47159         }
47160         
47161         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
47162         
47163         var keyPress = function(e){
47164             
47165             var k = e.getKey();
47166             
47167             var c = e.getCharCode();
47168             
47169             if(
47170                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
47171                     allowed.indexOf(String.fromCharCode(c)) === -1
47172             ){
47173                 e.stopEvent();
47174                 return;
47175             }
47176             
47177             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
47178                 return;
47179             }
47180             
47181             if(allowed.indexOf(String.fromCharCode(c)) === -1){
47182                 e.stopEvent();
47183             }
47184         };
47185         
47186         this.inputEl().on("keypress", keyPress, this);
47187         
47188     },
47189     
47190     onTriggerClick : function(e)
47191     {   
47192         if(this.disabled){
47193             return;
47194         }
47195         
47196         this.page = 0;
47197         this.loadNext = false;
47198         
47199         if(this.isExpanded()){
47200             this.collapse();
47201             return;
47202         }
47203         
47204         this.hasFocus = true;
47205         
47206         if(this.triggerAction == 'all') {
47207             this.doQuery(this.allQuery, true);
47208             return;
47209         }
47210         
47211         this.doQuery(this.getRawValue());
47212     },
47213     
47214     getCurrency : function()
47215     {   
47216         var v = this.currencyEl().getValue();
47217         
47218         return v;
47219     },
47220     
47221     restrictHeight : function()
47222     {
47223         this.list.alignTo(this.currencyEl(), this.listAlign);
47224         this.list.alignTo(this.currencyEl(), this.listAlign);
47225     },
47226     
47227     onViewClick : function(view, doFocus, el, e)
47228     {
47229         var index = this.view.getSelectedIndexes()[0];
47230         
47231         var r = this.store.getAt(index);
47232         
47233         if(r){
47234             this.onSelect(r, index);
47235         }
47236     },
47237     
47238     onSelect : function(record, index){
47239         
47240         if(this.fireEvent('beforeselect', this, record, index) !== false){
47241         
47242             this.setFromCurrencyData(index > -1 ? record.data : false);
47243             
47244             this.collapse();
47245             
47246             this.fireEvent('select', this, record, index);
47247         }
47248     },
47249     
47250     setFromCurrencyData : function(o)
47251     {
47252         var currency = '';
47253         
47254         this.lastCurrency = o;
47255         
47256         if (this.currencyField) {
47257             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47258         } else {
47259             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
47260         }
47261         
47262         this.lastSelectionText = currency;
47263         
47264         //setting default currency
47265         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47266             this.setCurrency(this.defaultCurrency);
47267             return;
47268         }
47269         
47270         this.setCurrency(currency);
47271     },
47272     
47273     setFromData : function(o)
47274     {
47275         var c = {};
47276         
47277         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47278         
47279         this.setFromCurrencyData(c);
47280         
47281         var value = '';
47282         
47283         if (this.name) {
47284             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47285         } else {
47286             Roo.log('no value set for '+ (this.name ? this.name : this.id));
47287         }
47288         
47289         this.setValue(value);
47290         
47291     },
47292     
47293     setCurrency : function(v)
47294     {   
47295         this.currencyValue = v;
47296         
47297         if(this.rendered){
47298             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47299             this.validate();
47300         }
47301     },
47302     
47303     setValue : function(v)
47304     {
47305         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47306         
47307         this.value = v;
47308         
47309         if(this.rendered){
47310             
47311             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47312             
47313             this.inputEl().dom.value = (v == '') ? '' :
47314                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47315             
47316             if(!this.allowZero && v === '0') {
47317                 this.hiddenEl().dom.value = '';
47318                 this.inputEl().dom.value = '';
47319             }
47320             
47321             this.validate();
47322         }
47323     },
47324     
47325     getRawValue : function()
47326     {
47327         var v = this.inputEl().getValue();
47328         
47329         return v;
47330     },
47331     
47332     getValue : function()
47333     {
47334         return this.fixPrecision(this.parseValue(this.getRawValue()));
47335     },
47336     
47337     parseValue : function(value)
47338     {
47339         if(this.thousandsDelimiter) {
47340             value += "";
47341             r = new RegExp(",", "g");
47342             value = value.replace(r, "");
47343         }
47344         
47345         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47346         return isNaN(value) ? '' : value;
47347         
47348     },
47349     
47350     fixPrecision : function(value)
47351     {
47352         if(this.thousandsDelimiter) {
47353             value += "";
47354             r = new RegExp(",", "g");
47355             value = value.replace(r, "");
47356         }
47357         
47358         var nan = isNaN(value);
47359         
47360         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47361             return nan ? '' : value;
47362         }
47363         return parseFloat(value).toFixed(this.decimalPrecision);
47364     },
47365     
47366     decimalPrecisionFcn : function(v)
47367     {
47368         return Math.floor(v);
47369     },
47370     
47371     validateValue : function(value)
47372     {
47373         if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47374             return false;
47375         }
47376         
47377         var num = this.parseValue(value);
47378         
47379         if(isNaN(num)){
47380             this.markInvalid(String.format(this.nanText, value));
47381             return false;
47382         }
47383         
47384         if(num < this.minValue){
47385             this.markInvalid(String.format(this.minText, this.minValue));
47386             return false;
47387         }
47388         
47389         if(num > this.maxValue){
47390             this.markInvalid(String.format(this.maxText, this.maxValue));
47391             return false;
47392         }
47393         
47394         return true;
47395     },
47396     
47397     validate : function()
47398     {
47399         if(this.disabled || this.allowBlank){
47400             this.markValid();
47401             return true;
47402         }
47403         
47404         var currency = this.getCurrency();
47405         
47406         if(this.validateValue(this.getRawValue()) && currency.length){
47407             this.markValid();
47408             return true;
47409         }
47410         
47411         this.markInvalid();
47412         return false;
47413     },
47414     
47415     getName: function()
47416     {
47417         return this.name;
47418     },
47419     
47420     beforeBlur : function()
47421     {
47422         if(!this.castInt){
47423             return;
47424         }
47425         
47426         var v = this.parseValue(this.getRawValue());
47427         
47428         if(v || v == 0){
47429             this.setValue(v);
47430         }
47431     },
47432     
47433     onBlur : function()
47434     {
47435         this.beforeBlur();
47436         
47437         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47438             //this.el.removeClass(this.focusClass);
47439         }
47440         
47441         this.hasFocus = false;
47442         
47443         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47444             this.validate();
47445         }
47446         
47447         var v = this.getValue();
47448         
47449         if(String(v) !== String(this.startValue)){
47450             this.fireEvent('change', this, v, this.startValue);
47451         }
47452         
47453         this.fireEvent("blur", this);
47454     },
47455     
47456     inputEl : function()
47457     {
47458         return this.el.select('.roo-money-amount-input', true).first();
47459     },
47460     
47461     currencyEl : function()
47462     {
47463         return this.el.select('.roo-money-currency-input', true).first();
47464     },
47465     
47466     hiddenEl : function()
47467     {
47468         return this.el.select('input.hidden-number-input',true).first();
47469     }
47470     
47471 });/**
47472  * @class Roo.bootstrap.BezierSignature
47473  * @extends Roo.bootstrap.Component
47474  * Bootstrap BezierSignature class
47475  * This script refer to:
47476  *    Title: Signature Pad
47477  *    Author: szimek
47478  *    Availability: https://github.com/szimek/signature_pad
47479  *
47480  * @constructor
47481  * Create a new BezierSignature
47482  * @param {Object} config The config object
47483  */
47484
47485 Roo.bootstrap.BezierSignature = function(config){
47486     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47487     this.addEvents({
47488         "resize" : true
47489     });
47490 };
47491
47492 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47493 {
47494      
47495     curve_data: [],
47496     
47497     is_empty: true,
47498     
47499     mouse_btn_down: true,
47500     
47501     /**
47502      * @cfg {int} canvas height
47503      */
47504     canvas_height: '200px',
47505     
47506     /**
47507      * @cfg {float|function} Radius of a single dot.
47508      */ 
47509     dot_size: false,
47510     
47511     /**
47512      * @cfg {float} Minimum width of a line. Defaults to 0.5.
47513      */
47514     min_width: 0.5,
47515     
47516     /**
47517      * @cfg {float} Maximum width of a line. Defaults to 2.5.
47518      */
47519     max_width: 2.5,
47520     
47521     /**
47522      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47523      */
47524     throttle: 16,
47525     
47526     /**
47527      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47528      */
47529     min_distance: 5,
47530     
47531     /**
47532      * @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.
47533      */
47534     bg_color: 'rgba(0, 0, 0, 0)',
47535     
47536     /**
47537      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47538      */
47539     dot_color: 'black',
47540     
47541     /**
47542      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47543      */ 
47544     velocity_filter_weight: 0.7,
47545     
47546     /**
47547      * @cfg {function} Callback when stroke begin. 
47548      */
47549     onBegin: false,
47550     
47551     /**
47552      * @cfg {function} Callback when stroke end.
47553      */
47554     onEnd: false,
47555     
47556     getAutoCreate : function()
47557     {
47558         var cls = 'roo-signature column';
47559         
47560         if(this.cls){
47561             cls += ' ' + this.cls;
47562         }
47563         
47564         var col_sizes = [
47565             'lg',
47566             'md',
47567             'sm',
47568             'xs'
47569         ];
47570         
47571         for(var i = 0; i < col_sizes.length; i++) {
47572             if(this[col_sizes[i]]) {
47573                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47574             }
47575         }
47576         
47577         var cfg = {
47578             tag: 'div',
47579             cls: cls,
47580             cn: [
47581                 {
47582                     tag: 'div',
47583                     cls: 'roo-signature-body',
47584                     cn: [
47585                         {
47586                             tag: 'canvas',
47587                             cls: 'roo-signature-body-canvas',
47588                             height: this.canvas_height,
47589                             width: this.canvas_width
47590                         }
47591                     ]
47592                 },
47593                 {
47594                     tag: 'input',
47595                     type: 'file',
47596                     style: 'display: none'
47597                 }
47598             ]
47599         };
47600         
47601         return cfg;
47602     },
47603     
47604     initEvents: function() 
47605     {
47606         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47607         
47608         var canvas = this.canvasEl();
47609         
47610         // mouse && touch event swapping...
47611         canvas.dom.style.touchAction = 'none';
47612         canvas.dom.style.msTouchAction = 'none';
47613         
47614         this.mouse_btn_down = false;
47615         canvas.on('mousedown', this._handleMouseDown, this);
47616         canvas.on('mousemove', this._handleMouseMove, this);
47617         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47618         
47619         if (window.PointerEvent) {
47620             canvas.on('pointerdown', this._handleMouseDown, this);
47621             canvas.on('pointermove', this._handleMouseMove, this);
47622             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47623         }
47624         
47625         if ('ontouchstart' in window) {
47626             canvas.on('touchstart', this._handleTouchStart, this);
47627             canvas.on('touchmove', this._handleTouchMove, this);
47628             canvas.on('touchend', this._handleTouchEnd, this);
47629         }
47630         
47631         Roo.EventManager.onWindowResize(this.resize, this, true);
47632         
47633         // file input event
47634         this.fileEl().on('change', this.uploadImage, this);
47635         
47636         this.clear();
47637         
47638         this.resize();
47639     },
47640     
47641     resize: function(){
47642         
47643         var canvas = this.canvasEl().dom;
47644         var ctx = this.canvasElCtx();
47645         var img_data = false;
47646         
47647         if(canvas.width > 0) {
47648             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47649         }
47650         // setting canvas width will clean img data
47651         canvas.width = 0;
47652         
47653         var style = window.getComputedStyle ? 
47654             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47655             
47656         var padding_left = parseInt(style.paddingLeft) || 0;
47657         var padding_right = parseInt(style.paddingRight) || 0;
47658         
47659         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47660         
47661         if(img_data) {
47662             ctx.putImageData(img_data, 0, 0);
47663         }
47664     },
47665     
47666     _handleMouseDown: function(e)
47667     {
47668         if (e.browserEvent.which === 1) {
47669             this.mouse_btn_down = true;
47670             this.strokeBegin(e);
47671         }
47672     },
47673     
47674     _handleMouseMove: function (e)
47675     {
47676         if (this.mouse_btn_down) {
47677             this.strokeMoveUpdate(e);
47678         }
47679     },
47680     
47681     _handleMouseUp: function (e)
47682     {
47683         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47684             this.mouse_btn_down = false;
47685             this.strokeEnd(e);
47686         }
47687     },
47688     
47689     _handleTouchStart: function (e) {
47690         
47691         e.preventDefault();
47692         if (e.browserEvent.targetTouches.length === 1) {
47693             // var touch = e.browserEvent.changedTouches[0];
47694             // this.strokeBegin(touch);
47695             
47696              this.strokeBegin(e); // assume e catching the correct xy...
47697         }
47698     },
47699     
47700     _handleTouchMove: function (e) {
47701         e.preventDefault();
47702         // var touch = event.targetTouches[0];
47703         // _this._strokeMoveUpdate(touch);
47704         this.strokeMoveUpdate(e);
47705     },
47706     
47707     _handleTouchEnd: function (e) {
47708         var wasCanvasTouched = e.target === this.canvasEl().dom;
47709         if (wasCanvasTouched) {
47710             e.preventDefault();
47711             // var touch = event.changedTouches[0];
47712             // _this._strokeEnd(touch);
47713             this.strokeEnd(e);
47714         }
47715     },
47716     
47717     reset: function () {
47718         this._lastPoints = [];
47719         this._lastVelocity = 0;
47720         this._lastWidth = (this.min_width + this.max_width) / 2;
47721         this.canvasElCtx().fillStyle = this.dot_color;
47722     },
47723     
47724     strokeMoveUpdate: function(e)
47725     {
47726         this.strokeUpdate(e);
47727         
47728         if (this.throttle) {
47729             this.throttleStroke(this.strokeUpdate, this.throttle);
47730         }
47731         else {
47732             this.strokeUpdate(e);
47733         }
47734     },
47735     
47736     strokeBegin: function(e)
47737     {
47738         var newPointGroup = {
47739             color: this.dot_color,
47740             points: []
47741         };
47742         
47743         if (typeof this.onBegin === 'function') {
47744             this.onBegin(e);
47745         }
47746         
47747         this.curve_data.push(newPointGroup);
47748         this.reset();
47749         this.strokeUpdate(e);
47750     },
47751     
47752     strokeUpdate: function(e)
47753     {
47754         var rect = this.canvasEl().dom.getBoundingClientRect();
47755         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47756         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47757         var lastPoints = lastPointGroup.points;
47758         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47759         var isLastPointTooClose = lastPoint
47760             ? point.distanceTo(lastPoint) <= this.min_distance
47761             : false;
47762         var color = lastPointGroup.color;
47763         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47764             var curve = this.addPoint(point);
47765             if (!lastPoint) {
47766                 this.drawDot({color: color, point: point});
47767             }
47768             else if (curve) {
47769                 this.drawCurve({color: color, curve: curve});
47770             }
47771             lastPoints.push({
47772                 time: point.time,
47773                 x: point.x,
47774                 y: point.y
47775             });
47776         }
47777     },
47778     
47779     strokeEnd: function(e)
47780     {
47781         this.strokeUpdate(e);
47782         if (typeof this.onEnd === 'function') {
47783             this.onEnd(e);
47784         }
47785     },
47786     
47787     addPoint:  function (point) {
47788         var _lastPoints = this._lastPoints;
47789         _lastPoints.push(point);
47790         if (_lastPoints.length > 2) {
47791             if (_lastPoints.length === 3) {
47792                 _lastPoints.unshift(_lastPoints[0]);
47793             }
47794             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47795             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47796             _lastPoints.shift();
47797             return curve;
47798         }
47799         return null;
47800     },
47801     
47802     calculateCurveWidths: function (startPoint, endPoint) {
47803         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47804             (1 - this.velocity_filter_weight) * this._lastVelocity;
47805
47806         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47807         var widths = {
47808             end: newWidth,
47809             start: this._lastWidth
47810         };
47811         
47812         this._lastVelocity = velocity;
47813         this._lastWidth = newWidth;
47814         return widths;
47815     },
47816     
47817     drawDot: function (_a) {
47818         var color = _a.color, point = _a.point;
47819         var ctx = this.canvasElCtx();
47820         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47821         ctx.beginPath();
47822         this.drawCurveSegment(point.x, point.y, width);
47823         ctx.closePath();
47824         ctx.fillStyle = color;
47825         ctx.fill();
47826     },
47827     
47828     drawCurve: function (_a) {
47829         var color = _a.color, curve = _a.curve;
47830         var ctx = this.canvasElCtx();
47831         var widthDelta = curve.endWidth - curve.startWidth;
47832         var drawSteps = Math.floor(curve.length()) * 2;
47833         ctx.beginPath();
47834         ctx.fillStyle = color;
47835         for (var i = 0; i < drawSteps; i += 1) {
47836         var t = i / drawSteps;
47837         var tt = t * t;
47838         var ttt = tt * t;
47839         var u = 1 - t;
47840         var uu = u * u;
47841         var uuu = uu * u;
47842         var x = uuu * curve.startPoint.x;
47843         x += 3 * uu * t * curve.control1.x;
47844         x += 3 * u * tt * curve.control2.x;
47845         x += ttt * curve.endPoint.x;
47846         var y = uuu * curve.startPoint.y;
47847         y += 3 * uu * t * curve.control1.y;
47848         y += 3 * u * tt * curve.control2.y;
47849         y += ttt * curve.endPoint.y;
47850         var width = curve.startWidth + ttt * widthDelta;
47851         this.drawCurveSegment(x, y, width);
47852         }
47853         ctx.closePath();
47854         ctx.fill();
47855     },
47856     
47857     drawCurveSegment: function (x, y, width) {
47858         var ctx = this.canvasElCtx();
47859         ctx.moveTo(x, y);
47860         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47861         this.is_empty = false;
47862     },
47863     
47864     clear: function()
47865     {
47866         var ctx = this.canvasElCtx();
47867         var canvas = this.canvasEl().dom;
47868         ctx.fillStyle = this.bg_color;
47869         ctx.clearRect(0, 0, canvas.width, canvas.height);
47870         ctx.fillRect(0, 0, canvas.width, canvas.height);
47871         this.curve_data = [];
47872         this.reset();
47873         this.is_empty = true;
47874     },
47875     
47876     fileEl: function()
47877     {
47878         return  this.el.select('input',true).first();
47879     },
47880     
47881     canvasEl: function()
47882     {
47883         return this.el.select('canvas',true).first();
47884     },
47885     
47886     canvasElCtx: function()
47887     {
47888         return this.el.select('canvas',true).first().dom.getContext('2d');
47889     },
47890     
47891     getImage: function(type)
47892     {
47893         if(this.is_empty) {
47894             return false;
47895         }
47896         
47897         // encryption ?
47898         return this.canvasEl().dom.toDataURL('image/'+type, 1);
47899     },
47900     
47901     drawFromImage: function(img_src)
47902     {
47903         var img = new Image();
47904         
47905         img.onload = function(){
47906             this.canvasElCtx().drawImage(img, 0, 0);
47907         }.bind(this);
47908         
47909         img.src = img_src;
47910         
47911         this.is_empty = false;
47912     },
47913     
47914     selectImage: function()
47915     {
47916         this.fileEl().dom.click();
47917     },
47918     
47919     uploadImage: function(e)
47920     {
47921         var reader = new FileReader();
47922         
47923         reader.onload = function(e){
47924             var img = new Image();
47925             img.onload = function(){
47926                 this.reset();
47927                 this.canvasElCtx().drawImage(img, 0, 0);
47928             }.bind(this);
47929             img.src = e.target.result;
47930         }.bind(this);
47931         
47932         reader.readAsDataURL(e.target.files[0]);
47933     },
47934     
47935     // Bezier Point Constructor
47936     Point: (function () {
47937         function Point(x, y, time) {
47938             this.x = x;
47939             this.y = y;
47940             this.time = time || Date.now();
47941         }
47942         Point.prototype.distanceTo = function (start) {
47943             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47944         };
47945         Point.prototype.equals = function (other) {
47946             return this.x === other.x && this.y === other.y && this.time === other.time;
47947         };
47948         Point.prototype.velocityFrom = function (start) {
47949             return this.time !== start.time
47950             ? this.distanceTo(start) / (this.time - start.time)
47951             : 0;
47952         };
47953         return Point;
47954     }()),
47955     
47956     
47957     // Bezier Constructor
47958     Bezier: (function () {
47959         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47960             this.startPoint = startPoint;
47961             this.control2 = control2;
47962             this.control1 = control1;
47963             this.endPoint = endPoint;
47964             this.startWidth = startWidth;
47965             this.endWidth = endWidth;
47966         }
47967         Bezier.fromPoints = function (points, widths, scope) {
47968             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47969             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47970             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47971         };
47972         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47973             var dx1 = s1.x - s2.x;
47974             var dy1 = s1.y - s2.y;
47975             var dx2 = s2.x - s3.x;
47976             var dy2 = s2.y - s3.y;
47977             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47978             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47979             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47980             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47981             var dxm = m1.x - m2.x;
47982             var dym = m1.y - m2.y;
47983             var k = l2 / (l1 + l2);
47984             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47985             var tx = s2.x - cm.x;
47986             var ty = s2.y - cm.y;
47987             return {
47988                 c1: new scope.Point(m1.x + tx, m1.y + ty),
47989                 c2: new scope.Point(m2.x + tx, m2.y + ty)
47990             };
47991         };
47992         Bezier.prototype.length = function () {
47993             var steps = 10;
47994             var length = 0;
47995             var px;
47996             var py;
47997             for (var i = 0; i <= steps; i += 1) {
47998                 var t = i / steps;
47999                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
48000                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
48001                 if (i > 0) {
48002                     var xdiff = cx - px;
48003                     var ydiff = cy - py;
48004                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
48005                 }
48006                 px = cx;
48007                 py = cy;
48008             }
48009             return length;
48010         };
48011         Bezier.prototype.point = function (t, start, c1, c2, end) {
48012             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
48013             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
48014             + (3.0 * c2 * (1.0 - t) * t * t)
48015             + (end * t * t * t);
48016         };
48017         return Bezier;
48018     }()),
48019     
48020     throttleStroke: function(fn, wait) {
48021       if (wait === void 0) { wait = 250; }
48022       var previous = 0;
48023       var timeout = null;
48024       var result;
48025       var storedContext;
48026       var storedArgs;
48027       var later = function () {
48028           previous = Date.now();
48029           timeout = null;
48030           result = fn.apply(storedContext, storedArgs);
48031           if (!timeout) {
48032               storedContext = null;
48033               storedArgs = [];
48034           }
48035       };
48036       return function wrapper() {
48037           var args = [];
48038           for (var _i = 0; _i < arguments.length; _i++) {
48039               args[_i] = arguments[_i];
48040           }
48041           var now = Date.now();
48042           var remaining = wait - (now - previous);
48043           storedContext = this;
48044           storedArgs = args;
48045           if (remaining <= 0 || remaining > wait) {
48046               if (timeout) {
48047                   clearTimeout(timeout);
48048                   timeout = null;
48049               }
48050               previous = now;
48051               result = fn.apply(storedContext, storedArgs);
48052               if (!timeout) {
48053                   storedContext = null;
48054                   storedArgs = [];
48055               }
48056           }
48057           else if (!timeout) {
48058               timeout = window.setTimeout(later, remaining);
48059           }
48060           return result;
48061       };
48062   }
48063   
48064 });
48065
48066  
48067
48068  // old names for form elements
48069 Roo.bootstrap.Form          =   Roo.bootstrap.form.Form;
48070 Roo.bootstrap.Input         =   Roo.bootstrap.form.Input;
48071 Roo.bootstrap.TextArea      =   Roo.bootstrap.form.TextArea;
48072 Roo.bootstrap.TriggerField  =   Roo.bootstrap.form.TriggerField;
48073 Roo.bootstrap.ComboBox      =   Roo.bootstrap.form.ComboBox;
48074 Roo.bootstrap.DateField     =   Roo.bootstrap.form.DateField;
48075 Roo.bootstrap.TimeField     =   Roo.bootstrap.form.TimeField;
48076 Roo.bootstrap.MonthField    =   Roo.bootstrap.form.MonthField;
48077 Roo.bootstrap.CheckBox      =   Roo.bootstrap.form.CheckBox;
48078 Roo.bootstrap.Radio         =   Roo.bootstrap.form.Radio;
48079 Roo.bootstrap.RadioSet      =   Roo.bootstrap.form.RadioSet;
48080 Roo.bootstrap.SecurePass    =   Roo.bootstrap.form.SecurePass;
48081 Roo.bootstrap.FieldLabel    =   Roo.bootstrap.form.FieldLabel;
48082 Roo.bootstrap.DateSplitField=   Roo.bootstrap.form.DateSplitField;
48083 Roo.bootstrap.NumberField   =   Roo.bootstrap.form.NumberField;
48084 Roo.bootstrap.PhoneInput    =   Roo.bootstrap.form.PhoneInput;
48085 Roo.bootstrap.PhoneInputData=   Roo.bootstrap.form.PhoneInputData;
48086 Roo.bootstrap.MoneyField    =   Roo.bootstrap.form.MoneyField;
48087 Roo.bootstrap.HtmlEditor    =   Roo.bootstrap.form.HtmlEditor;
48088 Roo.bootstrap.HtmlEditor.ToolbarStandard =   Roo.bootstrap.form.HtmlEditorToolbarStandard;
48089 Roo.bootstrap.Markdown      = Roo.bootstrap.form.Markdown;
48090 Roo.bootstrap.CardUploader  = Roo.bootstrap.form.CardUploader;// depricated.
48091 Roo.bootstrap.Navbar            = Roo.bootstrap.nav.Bar;
48092 Roo.bootstrap.NavGroup          = Roo.bootstrap.nav.Group;
48093 Roo.bootstrap.NavHeaderbar      = Roo.bootstrap.nav.Headerbar;
48094 Roo.bootstrap.NavItem           = Roo.bootstrap.nav.Item;
48095
48096 Roo.bootstrap.NavProgressBar     = Roo.bootstrap.nav.ProgressBar;
48097 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
48098
48099 Roo.bootstrap.NavSidebar        = Roo.bootstrap.nav.Sidebar;
48100 Roo.bootstrap.NavSidebarItem    = Roo.bootstrap.nav.SidebarItem;
48101
48102 Roo.bootstrap.NavSimplebar      = Roo.bootstrap.nav.Simplebar;// deprciated 
48103 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
48104 Roo.bootstrap.MenuItem =  Roo.bootstrap.menu.Item;
48105 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
48106